uboot/drivers/spi/meson_spifc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
   4 * Copyright (C) 2018 BayLibre, SAS
   5 * Author: Neil Armstrong <narmstrong@baylibre.com>
   6 *
   7 * Amlogic Meson SPI Flash Controller driver
   8 */
   9
  10#include <common.h>
  11#include <spi.h>
  12#include <clk.h>
  13#include <dm.h>
  14#include <regmap.h>
  15#include <errno.h>
  16#include <asm/io.h>
  17#include <linux/bitfield.h>
  18
  19/* register map */
  20#define REG_CMD                 0x00
  21#define REG_ADDR                0x04
  22#define REG_CTRL                0x08
  23#define REG_CTRL1               0x0c
  24#define REG_STATUS              0x10
  25#define REG_CTRL2               0x14
  26#define REG_CLOCK               0x18
  27#define REG_USER                0x1c
  28#define REG_USER1               0x20
  29#define REG_USER2               0x24
  30#define REG_USER3               0x28
  31#define REG_USER4               0x2c
  32#define REG_SLAVE               0x30
  33#define REG_SLAVE1              0x34
  34#define REG_SLAVE2              0x38
  35#define REG_SLAVE3              0x3c
  36#define REG_C0                  0x40
  37#define REG_B8                  0x60
  38#define REG_MAX                 0x7c
  39
  40/* register fields */
  41#define CMD_USER                BIT(18)
  42#define CTRL_ENABLE_AHB         BIT(17)
  43#define CLOCK_SOURCE            BIT(31)
  44#define CLOCK_DIV_SHIFT         12
  45#define CLOCK_DIV_MASK          (0x3f << CLOCK_DIV_SHIFT)
  46#define CLOCK_CNT_HIGH_SHIFT    6
  47#define CLOCK_CNT_HIGH_MASK     (0x3f << CLOCK_CNT_HIGH_SHIFT)
  48#define CLOCK_CNT_LOW_SHIFT     0
  49#define CLOCK_CNT_LOW_MASK      (0x3f << CLOCK_CNT_LOW_SHIFT)
  50#define USER_DIN_EN_MS          BIT(0)
  51#define USER_CMP_MODE           BIT(2)
  52#define USER_CLK_NOT_INV        BIT(7)
  53#define USER_UC_DOUT_SEL        BIT(27)
  54#define USER_UC_DIN_SEL         BIT(28)
  55#define USER_UC_MASK            ((BIT(5) - 1) << 27)
  56#define USER1_BN_UC_DOUT_SHIFT  17
  57#define USER1_BN_UC_DOUT_MASK   (0xff << 16)
  58#define USER1_BN_UC_DIN_SHIFT   8
  59#define USER1_BN_UC_DIN_MASK    (0xff << 8)
  60#define USER4_CS_POL_HIGH       BIT(23)
  61#define USER4_IDLE_CLK_HIGH     BIT(29)
  62#define USER4_CS_ACT            BIT(30)
  63#define SLAVE_TRST_DONE         BIT(4)
  64#define SLAVE_OP_MODE           BIT(30)
  65#define SLAVE_SW_RST            BIT(31)
  66
  67#define SPIFC_BUFFER_SIZE       64
  68
  69struct meson_spifc_priv {
  70        struct regmap                   *regmap;
  71        struct clk                      clk;
  72};
  73
  74/**
  75 * meson_spifc_drain_buffer() - copy data from device buffer to memory
  76 * @spifc:      the Meson SPI device
  77 * @buf:        the destination buffer
  78 * @len:        number of bytes to copy
  79 */
  80static void meson_spifc_drain_buffer(struct meson_spifc_priv *spifc,
  81                                     u8 *buf, int len)
  82{
  83        u32 data;
  84        int i = 0;
  85
  86        while (i < len) {
  87                regmap_read(spifc->regmap, REG_C0 + i, &data);
  88
  89                if (len - i >= 4) {
  90                        *((u32 *)buf) = data;
  91                        buf += 4;
  92                } else {
  93                        memcpy(buf, &data, len - i);
  94                        break;
  95                }
  96                i += 4;
  97        }
  98}
  99
 100/**
 101 * meson_spifc_fill_buffer() - copy data from memory to device buffer
 102 * @spifc:      the Meson SPI device
 103 * @buf:        the source buffer
 104 * @len:        number of bytes to copy
 105 */
 106static void meson_spifc_fill_buffer(struct meson_spifc_priv *spifc,
 107                                    const u8 *buf, int len)
 108{
 109        u32 data = 0;
 110        int i = 0;
 111
 112        while (i < len) {
 113                if (len - i >= 4)
 114                        data = *(u32 *)buf;
 115                else
 116                        memcpy(&data, buf, len - i);
 117
 118                regmap_write(spifc->regmap, REG_C0 + i, data);
 119
 120                buf += 4;
 121                i += 4;
 122        }
 123}
 124
 125/**
 126 * meson_spifc_txrx() - transfer a chunk of data
 127 * @spifc:      the Meson SPI device
 128 * @dout:       data buffer for TX
 129 * @din:        data buffer for RX
 130 * @offset:     offset of the data to transfer
 131 * @len:        length of the data to transfer
 132 * @last_xfer:  whether this is the last transfer of the message
 133 * @last_chunk: whether this is the last chunk of the transfer
 134 * Return:      0 on success, a negative value on error
 135 */
 136static int meson_spifc_txrx(struct meson_spifc_priv *spifc,
 137                            const u8 *dout, u8 *din, int offset,
 138                            int len, bool last_xfer, bool last_chunk)
 139{
 140        bool keep_cs = true;
 141        u32 data;
 142        int ret;
 143
 144        if (dout)
 145                meson_spifc_fill_buffer(spifc, dout + offset, len);
 146
 147        /* enable DOUT stage */
 148        regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK,
 149                           USER_UC_DOUT_SEL);
 150        regmap_write(spifc->regmap, REG_USER1,
 151                     (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT);
 152
 153        /* enable data input during DOUT */
 154        regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS,
 155                           USER_DIN_EN_MS);
 156
 157        if (last_chunk && last_xfer)
 158                keep_cs = false;
 159
 160        regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT,
 161                           keep_cs ? USER4_CS_ACT : 0);
 162
 163        /* clear transition done bit */
 164        regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0);
 165        /* start transfer */
 166        regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
 167
 168        /* wait for the current operation to terminate */
 169        ret = regmap_read_poll_timeout(spifc->regmap, REG_SLAVE, data,
 170                                       (data & SLAVE_TRST_DONE),
 171                                       0, 5 * CONFIG_SYS_HZ);
 172
 173        if (!ret && din)
 174                meson_spifc_drain_buffer(spifc, din + offset, len);
 175
 176        return ret;
 177}
 178
 179/**
 180 * meson_spifc_xfer() - perform a single transfer
 181 * @dev:        the SPI controller device
 182 * @bitlen:     length of the transfer
 183 * @dout:       data buffer for TX
 184 * @din:        data buffer for RX
 185 * @flags:      transfer flags
 186 * Return:      0 on success, a negative value on error
 187 */
 188static int meson_spifc_xfer(struct udevice *slave, unsigned int bitlen,
 189                            const void *dout, void *din, unsigned long flags)
 190{
 191        struct meson_spifc_priv *spifc = dev_get_priv(slave->parent);
 192        int blen = bitlen / 8;
 193        int len, done = 0, ret = 0;
 194
 195        if (bitlen % 8)
 196                return -EINVAL;
 197
 198        debug("xfer len %d (%d) dout %p din %p\n", bitlen, blen, dout, din);
 199
 200        regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0);
 201
 202        while (done < blen && !ret) {
 203                len = min_t(int, blen - done, SPIFC_BUFFER_SIZE);
 204                ret = meson_spifc_txrx(spifc, dout, din, done, len,
 205                                       flags & SPI_XFER_END,
 206                                       done + len >= blen);
 207                done += len;
 208        }
 209
 210        regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB,
 211                           CTRL_ENABLE_AHB);
 212
 213        return ret;
 214}
 215
 216/**
 217 * meson_spifc_set_speed() - program the clock divider
 218 * @dev:        the SPI controller device
 219 * @speed:      desired speed in Hz
 220 */
 221static int meson_spifc_set_speed(struct udevice *dev, uint speed)
 222{
 223        struct meson_spifc_priv *spifc = dev_get_priv(dev);
 224        unsigned long parent, value;
 225        int n;
 226
 227        parent = clk_get_rate(&spifc->clk);
 228        n = max_t(int, parent / speed - 1, 1);
 229
 230        debug("parent %lu, speed %u, n %d\n", parent, speed, n);
 231
 232        value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
 233        value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
 234        value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
 235                CLOCK_CNT_HIGH_MASK;
 236
 237        regmap_write(spifc->regmap, REG_CLOCK, value);
 238
 239        return 0;
 240}
 241
 242/**
 243 * meson_spifc_set_mode() - setups the SPI bus mode
 244 * @dev:        the SPI controller device
 245 * @mode:       desired mode bitfield
 246 * Return:      0 on success, -ENODEV on error
 247 */
 248static int meson_spifc_set_mode(struct udevice *dev, uint mode)
 249{
 250        struct meson_spifc_priv *spifc = dev_get_priv(dev);
 251
 252        if (mode & (SPI_CPHA | SPI_RX_QUAD | SPI_RX_DUAL |
 253                    SPI_TX_QUAD | SPI_TX_DUAL))
 254                return -ENODEV;
 255
 256        regmap_update_bits(spifc->regmap, REG_USER, USER_CLK_NOT_INV,
 257                           mode & SPI_CPOL ? USER_CLK_NOT_INV : 0);
 258
 259        regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_POL_HIGH,
 260                           mode & SPI_CS_HIGH ? USER4_CS_POL_HIGH : 0);
 261
 262        return 0;
 263}
 264
 265/**
 266 * meson_spifc_hw_init() - reset and initialize the SPI controller
 267 * @spifc:      the Meson SPI device
 268 */
 269static void meson_spifc_hw_init(struct meson_spifc_priv *spifc)
 270{
 271        /* reset device */
 272        regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST,
 273                           SLAVE_SW_RST);
 274        /* disable compatible mode */
 275        regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0);
 276        /* set master mode */
 277        regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0);
 278}
 279
 280static const struct dm_spi_ops meson_spifc_ops = {
 281        .xfer           = meson_spifc_xfer,
 282        .set_speed      = meson_spifc_set_speed,
 283        .set_mode       = meson_spifc_set_mode,
 284};
 285
 286static int meson_spifc_probe(struct udevice *dev)
 287{
 288        struct meson_spifc_priv *priv = dev_get_priv(dev);
 289        int ret;
 290
 291        ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
 292        if (ret)
 293                return ret;
 294
 295        ret = clk_get_by_index(dev, 0, &priv->clk);
 296        if (ret)
 297                return ret;
 298
 299        ret = clk_enable(&priv->clk);
 300        if (ret)
 301                return ret;
 302
 303        meson_spifc_hw_init(priv);
 304
 305        return 0;
 306}
 307
 308static const struct udevice_id meson_spifc_ids[] = {
 309        { .compatible = "amlogic,meson-gxbb-spifc", },
 310        { }
 311};
 312
 313U_BOOT_DRIVER(meson_spifc) = {
 314        .name           = "meson_spifc",
 315        .id             = UCLASS_SPI,
 316        .of_match       = meson_spifc_ids,
 317        .ops            = &meson_spifc_ops,
 318        .probe          = meson_spifc_probe,
 319        .priv_auto_alloc_size = sizeof(struct meson_spifc_priv),
 320};
 321