uboot/drivers/spi/mt7621_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018 Stefan Roese <sr@denx.de>
   4 *
   5 * Derived from the Linux driver version drivers/spi/spi-mt7621.c
   6 *   Copyright (C) 2011 Sergiy <piratfm@gmail.com>
   7 *   Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
   8 *   Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name>
   9 */
  10
  11#include <common.h>
  12#include <clk.h>
  13#include <dm.h>
  14#include <spi.h>
  15#include <wait_bit.h>
  16#include <linux/io.h>
  17
  18#define MT7621_RX_FIFO_LEN      32
  19#define MT7621_TX_FIFO_LEN      36
  20
  21#define MT7621_SPI_TRANS        0x00
  22#define MT7621_SPI_TRANS_START  BIT(8)
  23#define MT7621_SPI_TRANS_BUSY   BIT(16)
  24#define TRANS_ADDR_SZ           GENMASK(20, 19)
  25#define TRANS_ADDR_SZ_SHIFT     19
  26#define TRANS_MOSI_BCNT         GENMASK(3, 0)
  27#define TRANS_MOSI_BCNT_SHIFT   0
  28
  29#define MT7621_SPI_OPCODE       0x04
  30#define MT7621_SPI_DATA0        0x08
  31#define MT7621_SPI_DATA4        0x18
  32#define MT7621_SPI_MASTER       0x28
  33#define MT7621_SPI_MOREBUF      0x2c
  34#define MT7621_SPI_POLAR        0x38
  35
  36#define MT7621_LSB_FIRST        BIT(3)
  37#define MT7621_CPOL             BIT(4)
  38#define MT7621_CPHA             BIT(5)
  39
  40#define MASTER_MORE_BUFMODE     BIT(2)
  41#define MASTER_RS_CLK_SEL       GENMASK(27, 16)
  42#define MASTER_RS_CLK_SEL_SHIFT 16
  43#define MASTER_RS_SLAVE_SEL     GENMASK(31, 29)
  44
  45#define MOREBUF_CMD_CNT         GENMASK(29, 24)
  46#define MOREBUF_CMD_CNT_SHIFT   24
  47#define MOREBUF_MISO_CNT        GENMASK(20, 12)
  48#define MOREBUF_MISO_CNT_SHIFT  12
  49#define MOREBUF_MOSI_CNT        GENMASK(8, 0)
  50#define MOREBUF_MOSI_CNT_SHIFT  0
  51
  52struct mt7621_spi {
  53        void __iomem *base;
  54        unsigned int sys_freq;
  55};
  56
  57static void mt7621_spi_set_cs(struct mt7621_spi *rs, int cs, int enable)
  58{
  59        debug("%s: cs#%d -> %s\n", __func__, cs, enable ? "enable" : "disable");
  60
  61        if (enable) {
  62                setbits_le32(rs->base + MT7621_SPI_MASTER,
  63                             MASTER_RS_SLAVE_SEL | MASTER_MORE_BUFMODE);
  64                iowrite32(BIT(cs), rs->base + MT7621_SPI_POLAR);
  65        } else {
  66                iowrite32(0, rs->base + MT7621_SPI_POLAR);
  67                iowrite32((2 << TRANS_ADDR_SZ_SHIFT) |
  68                          (1 << TRANS_MOSI_BCNT_SHIFT),
  69                          rs->base + MT7621_SPI_TRANS);
  70                clrbits_le32(rs->base + MT7621_SPI_MASTER,
  71                             MASTER_RS_SLAVE_SEL | MASTER_MORE_BUFMODE);
  72        }
  73}
  74
  75static int mt7621_spi_set_mode(struct udevice *bus, uint mode)
  76{
  77        struct mt7621_spi *rs = dev_get_priv(bus);
  78        u32 reg;
  79
  80        debug("%s: mode=0x%08x\n", __func__, mode);
  81        reg = ioread32(rs->base + MT7621_SPI_MASTER);
  82
  83        reg &= ~MT7621_LSB_FIRST;
  84        if (mode & SPI_LSB_FIRST)
  85                reg |= MT7621_LSB_FIRST;
  86
  87        reg &= ~(MT7621_CPHA | MT7621_CPOL);
  88        switch (mode & (SPI_CPOL | SPI_CPHA)) {
  89        case SPI_MODE_0:
  90                break;
  91        case SPI_MODE_1:
  92                reg |= MT7621_CPHA;
  93                break;
  94        case SPI_MODE_2:
  95                reg |= MT7621_CPOL;
  96                break;
  97        case SPI_MODE_3:
  98                reg |= MT7621_CPOL | MT7621_CPHA;
  99                break;
 100        }
 101        iowrite32(reg, rs->base + MT7621_SPI_MASTER);
 102
 103        return 0;
 104}
 105
 106static int mt7621_spi_set_speed(struct udevice *bus, uint speed)
 107{
 108        struct mt7621_spi *rs = dev_get_priv(bus);
 109        u32 rate;
 110        u32 reg;
 111
 112        debug("%s: speed=%d\n", __func__, speed);
 113        rate = DIV_ROUND_UP(rs->sys_freq, speed);
 114        debug("rate:%u\n", rate);
 115
 116        if (rate > 4097)
 117                return -EINVAL;
 118
 119        if (rate < 2)
 120                rate = 2;
 121
 122        reg = ioread32(rs->base + MT7621_SPI_MASTER);
 123        reg &= ~MASTER_RS_CLK_SEL;
 124        reg |= (rate - 2) << MASTER_RS_CLK_SEL_SHIFT;
 125        iowrite32(reg, rs->base + MT7621_SPI_MASTER);
 126
 127        return 0;
 128}
 129
 130static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs)
 131{
 132        int ret;
 133
 134        ret =  wait_for_bit_le32(rs->base + MT7621_SPI_TRANS,
 135                                 MT7621_SPI_TRANS_BUSY, 0, 10, 0);
 136        if (ret)
 137                pr_err("Timeout in %s!\n", __func__);
 138
 139        return ret;
 140}
 141
 142static int mt7621_spi_read(struct mt7621_spi *rs, u8 *buf, size_t len)
 143{
 144        size_t rx_len;
 145        int i, ret;
 146        u32 val = 0;
 147
 148        while (len) {
 149                rx_len = min_t(size_t, len, MT7621_RX_FIFO_LEN);
 150
 151                iowrite32((rx_len * 8) << MOREBUF_MISO_CNT_SHIFT,
 152                          rs->base + MT7621_SPI_MOREBUF);
 153                iowrite32(MT7621_SPI_TRANS_START, rs->base + MT7621_SPI_TRANS);
 154
 155                ret = mt7621_spi_wait_till_ready(rs);
 156                if (ret)
 157                        return ret;
 158
 159                for (i = 0; i < rx_len; i++) {
 160                        if ((i % 4) == 0)
 161                                val = ioread32(rs->base + MT7621_SPI_DATA0 + i);
 162                        *buf++ = val & 0xff;
 163                        val >>= 8;
 164                }
 165
 166                len -= rx_len;
 167        }
 168
 169        return ret;
 170}
 171
 172static int mt7621_spi_write(struct mt7621_spi *rs, const u8 *buf, size_t len)
 173{
 174        size_t tx_len, opcode_len, dido_len;
 175        int i, ret;
 176        u32 val;
 177
 178        while (len) {
 179                tx_len = min_t(size_t, len, MT7621_TX_FIFO_LEN);
 180
 181                opcode_len = min_t(size_t, tx_len, 4);
 182                dido_len = tx_len - opcode_len;
 183
 184                val = 0;
 185                for (i = 0; i < opcode_len; i++) {
 186                        val <<= 8;
 187                        val |= *buf++;
 188                }
 189
 190                iowrite32(val, rs->base + MT7621_SPI_OPCODE);
 191
 192                val = 0;
 193                for (i = 0; i < dido_len; i++) {
 194                        val |= (*buf++) << ((i % 4) * 8);
 195
 196                        if ((i % 4 == 3) || (i == dido_len - 1)) {
 197                                iowrite32(val, rs->base + MT7621_SPI_DATA0 +
 198                                          (i & ~3));
 199                                val = 0;
 200                        }
 201                }
 202
 203                iowrite32(((opcode_len * 8) << MOREBUF_CMD_CNT_SHIFT) |
 204                          ((dido_len * 8) << MOREBUF_MOSI_CNT_SHIFT),
 205                          rs->base + MT7621_SPI_MOREBUF);
 206                iowrite32(MT7621_SPI_TRANS_START, rs->base + MT7621_SPI_TRANS);
 207
 208                ret = mt7621_spi_wait_till_ready(rs);
 209                if (ret)
 210                        return ret;
 211
 212                len -= tx_len;
 213        }
 214
 215        return 0;
 216}
 217
 218static int mt7621_spi_xfer(struct udevice *dev, unsigned int bitlen,
 219                           const void *dout, void *din, unsigned long flags)
 220{
 221        struct udevice *bus = dev->parent;
 222        struct mt7621_spi *rs = dev_get_priv(bus);
 223        int total_size = bitlen >> 3;
 224        int ret = 0;
 225
 226        debug("%s: dout=%p, din=%p, len=%x, flags=%lx\n", __func__, dout, din,
 227              total_size, flags);
 228
 229        /*
 230         * This driver only supports half-duplex, so complain and bail out
 231         * upon full-duplex messages
 232         */
 233        if (dout && din) {
 234                printf("Only half-duplex SPI transfer supported\n");
 235                return -EIO;
 236        }
 237
 238        mt7621_spi_wait_till_ready(rs);
 239
 240        /*
 241         * Set CS active upon start of SPI message. This message can
 242         * be split upon multiple calls to this xfer function
 243         */
 244        if (flags & SPI_XFER_BEGIN)
 245                mt7621_spi_set_cs(rs, spi_chip_select(dev), 1);
 246
 247        if (din)
 248                ret = mt7621_spi_read(rs, din, total_size);
 249        else if (dout)
 250                ret = mt7621_spi_write(rs, dout, total_size);
 251
 252        if (flags & SPI_XFER_END)
 253                mt7621_spi_set_cs(rs, spi_chip_select(dev), 0);
 254
 255        return ret;
 256}
 257
 258static int mt7621_spi_probe(struct udevice *dev)
 259{
 260        struct mt7621_spi *rs = dev_get_priv(dev);
 261        struct clk clk;
 262        int ret;
 263
 264        rs->base = dev_remap_addr(dev);
 265        if (!rs->base)
 266                return -EINVAL;
 267
 268        ret = clk_get_by_index(dev, 0, &clk);
 269        if (ret < 0) {
 270                printf("Please provide a clock!\n");
 271                return ret;
 272        }
 273
 274        clk_enable(&clk);
 275
 276        rs->sys_freq = clk_get_rate(&clk);
 277        if (!rs->sys_freq) {
 278                printf("Please provide a valid clock!\n");
 279                return -EINVAL;
 280        }
 281
 282        return 0;
 283}
 284
 285static const struct dm_spi_ops mt7621_spi_ops = {
 286        .set_mode = mt7621_spi_set_mode,
 287        .set_speed = mt7621_spi_set_speed,
 288        .xfer = mt7621_spi_xfer,
 289        /*
 290         * cs_info is not needed, since we require all chip selects to be
 291         * in the device tree explicitly
 292         */
 293};
 294
 295static const struct udevice_id mt7621_spi_ids[] = {
 296        { .compatible = "ralink,mt7621-spi" },
 297        { }
 298};
 299
 300U_BOOT_DRIVER(mt7621_spi) = {
 301        .name = "mt7621_spi",
 302        .id = UCLASS_SPI,
 303        .of_match = mt7621_spi_ids,
 304        .ops = &mt7621_spi_ops,
 305        .priv_auto_alloc_size = sizeof(struct mt7621_spi),
 306        .probe = mt7621_spi_probe,
 307};
 308