uboot/drivers/spi/mt7620_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2020 MediaTek Inc.
   4 *
   5 * Author: Weijie Gao <weijie.gao@mediatek.com>
   6 *
   7 * Generic SPI driver for MediaTek MT7620 SoC
   8 */
   9
  10#include <clk.h>
  11#include <dm.h>
  12#include <dm/device_compat.h>
  13#include <spi.h>
  14#include <linux/bitops.h>
  15#include <linux/iopoll.h>
  16#include <linux/io.h>
  17#include <linux/log2.h>
  18
  19#define MT7620_SPI_NUM_CS       2
  20#define MT7620_SPI_MASTER1_OFF  0x00
  21#define MT7620_SPI_MASTER2_OFF  0x40
  22
  23/* SPI_STAT */
  24#define   SPI_BUSY              BIT(0)
  25
  26/* SPI_CFG */
  27#define   MSB_FIRST             BIT(8)
  28#define   SPI_CLK_POL           BIT(6)
  29#define   RX_CLK_EDGE           BIT(5)
  30#define   TX_CLK_EDGE           BIT(4)
  31#define   SPI_CLK_S             0
  32#define   SPI_CLK_M             GENMASK(2, 0)
  33
  34/* SPI_CTL */
  35#define   START_WR              BIT(2)
  36#define   START_RD              BIT(1)
  37#define   SPI_HIGH              BIT(0)
  38
  39#define SPI_ARB                 0xf0
  40#define   ARB_EN                BIT(31)
  41
  42#define POLLING_SCALE           10
  43#define POLLING_FRAC_USEC       100
  44
  45struct mt7620_spi_master_regs {
  46        u32 stat;
  47        u32 reserved0[3];
  48        u32 cfg;
  49        u32 ctl;
  50        u32 reserved1[2];
  51        u32 data;
  52};
  53
  54struct mt7620_spi {
  55        void __iomem *regs;
  56        struct mt7620_spi_master_regs *m[MT7620_SPI_NUM_CS];
  57        unsigned int sys_freq;
  58        u32 wait_us;
  59        uint mode;
  60        uint speed;
  61};
  62
  63static void mt7620_spi_master_setup(struct mt7620_spi *ms, int cs)
  64{
  65        u32 rate, prescale, freq, tmo, cfg;
  66
  67        /* Calculate the clock divsior */
  68        rate = DIV_ROUND_UP(ms->sys_freq, ms->speed);
  69        rate = roundup_pow_of_two(rate);
  70
  71        prescale = ilog2(rate / 2);
  72        if (prescale > 6)
  73                prescale = 6;
  74
  75        /* Calculate the real clock, and usecs for one byte transaction */
  76        freq = ms->sys_freq >> (prescale + 1);
  77        tmo = DIV_ROUND_UP(8 * 1000000, freq);
  78
  79        /* 10 times tolerance plus 100us */
  80        ms->wait_us = POLLING_SCALE * tmo + POLLING_FRAC_USEC;
  81
  82        /* set SPI_CFG */
  83        cfg = prescale << SPI_CLK_S;
  84
  85        switch (ms->mode & (SPI_CPOL | SPI_CPHA)) {
  86        case SPI_MODE_0:
  87                cfg |= TX_CLK_EDGE;
  88                break;
  89        case SPI_MODE_1:
  90                cfg |= RX_CLK_EDGE;
  91                break;
  92        case SPI_MODE_2:
  93                cfg |= SPI_CLK_POL | RX_CLK_EDGE;
  94                break;
  95        case SPI_MODE_3:
  96                cfg |= SPI_CLK_POL | TX_CLK_EDGE;
  97                break;
  98        }
  99
 100        if (!(ms->mode & SPI_LSB_FIRST))
 101                cfg |= MSB_FIRST;
 102
 103        writel(cfg, &ms->m[cs]->cfg);
 104
 105        writel(SPI_HIGH, &ms->m[cs]->ctl);
 106}
 107
 108static void mt7620_spi_set_cs(struct mt7620_spi *ms, int cs, bool enable)
 109{
 110        if (enable)
 111                mt7620_spi_master_setup(ms, cs);
 112
 113        if (ms->mode & SPI_CS_HIGH)
 114                enable = !enable;
 115
 116        if (enable)
 117                clrbits_32(&ms->m[cs]->ctl, SPI_HIGH);
 118        else
 119                setbits_32(&ms->m[cs]->ctl, SPI_HIGH);
 120}
 121
 122static int mt7620_spi_set_mode(struct udevice *bus, uint mode)
 123{
 124        struct mt7620_spi *ms = dev_get_priv(bus);
 125
 126        ms->mode = mode;
 127
 128        /* Mode 0 is buggy. Force to use mode 3 */
 129        if ((mode & SPI_MODE_3) == SPI_MODE_0)
 130                ms->mode |= SPI_MODE_3;
 131
 132        return 0;
 133}
 134
 135static int mt7620_spi_set_speed(struct udevice *bus, uint speed)
 136{
 137        struct mt7620_spi *ms = dev_get_priv(bus);
 138
 139        ms->speed = speed;
 140
 141        return 0;
 142}
 143
 144static inline int mt7620_spi_busy_poll(struct mt7620_spi *ms, int cs)
 145{
 146        u32 val;
 147
 148        return readl_poll_timeout(&ms->m[cs]->stat, val, !(val & SPI_BUSY),
 149                                  ms->wait_us);
 150}
 151
 152static int mt7620_spi_read(struct mt7620_spi *ms, int cs, u8 *buf, size_t len)
 153{
 154        int ret;
 155
 156        while (len) {
 157                setbits_32(&ms->m[cs]->ctl, START_RD);
 158
 159                ret = mt7620_spi_busy_poll(ms, cs);
 160                if (ret)
 161                        return ret;
 162
 163                *buf++ = (u8)readl(&ms->m[cs]->data);
 164
 165                len--;
 166        }
 167
 168        return 0;
 169}
 170
 171static int mt7620_spi_write(struct mt7620_spi *ms, int cs, const u8 *buf,
 172                            size_t len)
 173{
 174        int ret;
 175
 176        while (len) {
 177                writel(*buf++, &ms->m[cs]->data);
 178                setbits_32(&ms->m[cs]->ctl, START_WR);
 179
 180                ret = mt7620_spi_busy_poll(ms, cs);
 181                if (ret)
 182                        return ret;
 183
 184                len--;
 185        }
 186
 187        return 0;
 188}
 189
 190static int mt7620_spi_xfer(struct udevice *dev, unsigned int bitlen,
 191                           const void *dout, void *din, unsigned long flags)
 192{
 193        struct udevice *bus = dev->parent;
 194        struct mt7620_spi *ms = dev_get_priv(bus);
 195        int total_size = bitlen >> 3;
 196        int cs, ret = 0;
 197
 198        /*
 199         * This driver only supports half-duplex, so complain and bail out
 200         * upon full-duplex messages
 201         */
 202        if (dout && din) {
 203                dev_err(dev, "mt7620_spi: Only half-duplex is supported\n");
 204                return -EIO;
 205        }
 206
 207        cs = spi_chip_select(dev);
 208        if (cs < 0 || cs >= MT7620_SPI_NUM_CS) {
 209                dev_err(dev, "mt7620_spi: Invalid chip select %d\n", cs);
 210                return -EINVAL;
 211        }
 212
 213        if (flags & SPI_XFER_BEGIN)
 214                mt7620_spi_set_cs(ms, cs, true);
 215
 216        if (din)
 217                ret = mt7620_spi_read(ms, cs, din, total_size);
 218        else if (dout)
 219                ret = mt7620_spi_write(ms, cs, dout, total_size);
 220
 221        if (ret)
 222                dev_err(dev, "mt7620_spi: %s transaction timeout\n",
 223                        din ? "read" : "write");
 224
 225        if (flags & SPI_XFER_END)
 226                mt7620_spi_set_cs(ms, cs, false);
 227
 228        return ret;
 229}
 230
 231static int mt7620_spi_probe(struct udevice *dev)
 232{
 233        struct mt7620_spi *ms = dev_get_priv(dev);
 234        struct clk clk;
 235        int ret;
 236
 237        ms->regs = dev_remap_addr(dev);
 238        if (!ms->regs)
 239                return -EINVAL;
 240
 241        ms->m[0] = ms->regs + MT7620_SPI_MASTER1_OFF;
 242        ms->m[1] = ms->regs + MT7620_SPI_MASTER2_OFF;
 243
 244        ret = clk_get_by_index(dev, 0, &clk);
 245        if (ret < 0) {
 246                dev_err(dev, "mt7620_spi: Please provide a clock!\n");
 247                return ret;
 248        }
 249
 250        clk_enable(&clk);
 251
 252        ms->sys_freq = clk_get_rate(&clk);
 253        if (!ms->sys_freq) {
 254                dev_err(dev, "mt7620_spi: Please provide a valid bus clock!\n");
 255                return -EINVAL;
 256        }
 257
 258        writel(ARB_EN, ms->regs + SPI_ARB);
 259
 260        return 0;
 261}
 262
 263static const struct dm_spi_ops mt7620_spi_ops = {
 264        .set_mode = mt7620_spi_set_mode,
 265        .set_speed = mt7620_spi_set_speed,
 266        .xfer = mt7620_spi_xfer,
 267};
 268
 269static const struct udevice_id mt7620_spi_ids[] = {
 270        { .compatible = "mediatek,mt7620-spi" },
 271        { }
 272};
 273
 274U_BOOT_DRIVER(mt7620_spi) = {
 275        .name = "mt7620_spi",
 276        .id = UCLASS_SPI,
 277        .of_match = mt7620_spi_ids,
 278        .ops = &mt7620_spi_ops,
 279        .priv_auto = sizeof(struct mt7620_spi),
 280        .probe = mt7620_spi_probe,
 281};
 282