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