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