uboot/drivers/spi/mvebu_a3700_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2015 Marvell International Ltd.
   4 *
   5 * Copyright (C) 2016 Stefan Roese <sr@denx.de>
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <malloc.h>
  11#include <spi.h>
  12#include <clk.h>
  13#include <wait_bit.h>
  14#include <asm/io.h>
  15
  16DECLARE_GLOBAL_DATA_PTR;
  17
  18#define MVEBU_SPI_A3700_XFER_RDY                BIT(1)
  19#define MVEBU_SPI_A3700_FIFO_FLUSH              BIT(9)
  20#define MVEBU_SPI_A3700_BYTE_LEN                BIT(5)
  21#define MVEBU_SPI_A3700_CLK_PHA                 BIT(6)
  22#define MVEBU_SPI_A3700_CLK_POL                 BIT(7)
  23#define MVEBU_SPI_A3700_FIFO_EN                 BIT(17)
  24#define MVEBU_SPI_A3700_SPI_EN_0                BIT(16)
  25#define MVEBU_SPI_A3700_CLK_PRESCALE_MASK       0x1f
  26
  27
  28/* SPI registers */
  29struct spi_reg {
  30        u32 ctrl;       /* 0x10600 */
  31        u32 cfg;        /* 0x10604 */
  32        u32 dout;       /* 0x10608 */
  33        u32 din;        /* 0x1060c */
  34};
  35
  36struct mvebu_spi_platdata {
  37        struct spi_reg *spireg;
  38        struct clk clk;
  39};
  40
  41static void spi_cs_activate(struct spi_reg *reg, int cs)
  42{
  43        setbits_le32(&reg->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs);
  44}
  45
  46static void spi_cs_deactivate(struct spi_reg *reg, int cs)
  47{
  48        clrbits_le32(&reg->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs);
  49}
  50
  51/**
  52 * spi_legacy_shift_byte() - triggers the real SPI transfer
  53 * @bytelen:    Indicate how many bytes to transfer.
  54 * @dout:       Buffer address of what to send.
  55 * @din:        Buffer address of where to receive.
  56 *
  57 * This function triggers the real SPI transfer in legacy mode. It
  58 * will shift out char buffer from @dout, and shift in char buffer to
  59 * @din, if necessary.
  60 *
  61 * This function assumes that only one byte is shifted at one time.
  62 * However, it is not its responisbility to set the transfer type to
  63 * one-byte. Also, it does not guarantee that it will work if transfer
  64 * type becomes two-byte. See spi_set_legacy() for details.
  65 *
  66 * In legacy mode, simply write to the SPI_DOUT register will trigger
  67 * the transfer.
  68 *
  69 * If @dout == NULL, which means no actual data needs to be sent out,
  70 * then the function will shift out 0x00 in order to shift in data.
  71 * The XFER_RDY flag is checked every time before accessing SPI_DOUT
  72 * and SPI_DIN register.
  73 *
  74 * The number of transfers to be triggerred is decided by @bytelen.
  75 *
  76 * Return:      0 - cool
  77 *              -ETIMEDOUT - XFER_RDY flag timeout
  78 */
  79static int spi_legacy_shift_byte(struct spi_reg *reg, unsigned int bytelen,
  80                                 const void *dout, void *din)
  81{
  82        const u8 *dout_8;
  83        u8 *din_8;
  84        int ret;
  85
  86        /* Use 0x00 as dummy dout */
  87        const u8 dummy_dout = 0x0;
  88        u32 pending_dout = 0x0;
  89
  90        /* dout_8: pointer of current dout */
  91        dout_8 = dout;
  92        /* din_8: pointer of current din */
  93        din_8 = din;
  94
  95        while (bytelen) {
  96                ret = wait_for_bit_le32(&reg->ctrl,
  97                                        MVEBU_SPI_A3700_XFER_RDY,
  98                                        true,100, false);
  99                if (ret)
 100                        return ret;
 101
 102                if (dout)
 103                        pending_dout = (u32)*dout_8;
 104                else
 105                        pending_dout = (u32)dummy_dout;
 106
 107                /* Trigger the xfer */
 108                writel(pending_dout, &reg->dout);
 109
 110                if (din) {
 111                        ret = wait_for_bit_le32(&reg->ctrl,
 112                                                MVEBU_SPI_A3700_XFER_RDY,
 113                                                true, 100, false);
 114                        if (ret)
 115                                return ret;
 116
 117                        /* Read what is transferred in */
 118                        *din_8 = (u8)readl(&reg->din);
 119                }
 120
 121                /* Don't increment the current pointer if NULL */
 122                if (dout)
 123                        dout_8++;
 124                if (din)
 125                        din_8++;
 126
 127                bytelen--;
 128        }
 129
 130        return 0;
 131}
 132
 133static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
 134                          const void *dout, void *din, unsigned long flags)
 135{
 136        struct udevice *bus = dev->parent;
 137        struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 138        struct spi_reg *reg = plat->spireg;
 139        unsigned int bytelen;
 140        int ret;
 141
 142        bytelen = bitlen / 8;
 143
 144        if (dout && din)
 145                debug("This is a duplex transfer.\n");
 146
 147        /* Activate CS */
 148        if (flags & SPI_XFER_BEGIN) {
 149                debug("SPI: activate cs.\n");
 150                spi_cs_activate(reg, spi_chip_select(dev));
 151        }
 152
 153        /* Send and/or receive */
 154        if (dout || din) {
 155                ret = spi_legacy_shift_byte(reg, bytelen, dout, din);
 156                if (ret)
 157                        return ret;
 158        }
 159
 160        /* Deactivate CS */
 161        if (flags & SPI_XFER_END) {
 162                ret = wait_for_bit_le32(&reg->ctrl,
 163                                        MVEBU_SPI_A3700_XFER_RDY,
 164                                        true, 100, false);
 165                if (ret)
 166                        return ret;
 167
 168                debug("SPI: deactivate cs.\n");
 169                spi_cs_deactivate(reg, spi_chip_select(dev));
 170        }
 171
 172        return 0;
 173}
 174
 175static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
 176{
 177        struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 178        struct spi_reg *reg = plat->spireg;
 179        u32 data, prescale;
 180
 181        data = readl(&reg->cfg);
 182
 183        prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz);
 184        if (prescale > 0x1f)
 185                prescale = 0x1f;
 186        else if (prescale > 0xf)
 187                prescale = 0x10 + (prescale + 1) / 2;
 188
 189        data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
 190        data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
 191
 192        writel(data, &reg->cfg);
 193
 194        return 0;
 195}
 196
 197static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
 198{
 199        struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 200        struct spi_reg *reg = plat->spireg;
 201
 202        /*
 203         * Set SPI polarity
 204         * 0: Serial interface clock is low when inactive
 205         * 1: Serial interface clock is high when inactive
 206         */
 207        if (mode & SPI_CPOL)
 208                setbits_le32(&reg->cfg, MVEBU_SPI_A3700_CLK_POL);
 209        else
 210                clrbits_le32(&reg->cfg, MVEBU_SPI_A3700_CLK_POL);
 211        if (mode & SPI_CPHA)
 212                setbits_le32(&reg->cfg, MVEBU_SPI_A3700_CLK_PHA);
 213        else
 214                clrbits_le32(&reg->cfg, MVEBU_SPI_A3700_CLK_PHA);
 215
 216        return 0;
 217}
 218
 219static int mvebu_spi_probe(struct udevice *bus)
 220{
 221        struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 222        struct spi_reg *reg = plat->spireg;
 223        u32 data;
 224        int ret;
 225
 226        /*
 227         * Settings SPI controller to be working in legacy mode, which
 228         * means use only DO pin (I/O 1) for Data Out, and DI pin (I/O 0)
 229         * for Data In.
 230         */
 231
 232        /* Flush read/write FIFO */
 233        data = readl(&reg->cfg);
 234        writel(data | MVEBU_SPI_A3700_FIFO_FLUSH, &reg->cfg);
 235        ret = wait_for_bit_le32(&reg->cfg, MVEBU_SPI_A3700_FIFO_FLUSH,
 236                                false, 1000, false);
 237        if (ret)
 238                return ret;
 239
 240        /* Disable FIFO mode */
 241        data &= ~MVEBU_SPI_A3700_FIFO_EN;
 242
 243        /* Always shift 1 byte at a time */
 244        data &= ~MVEBU_SPI_A3700_BYTE_LEN;
 245
 246        writel(data, &reg->cfg);
 247
 248        return 0;
 249}
 250
 251static int mvebu_spi_ofdata_to_platdata(struct udevice *bus)
 252{
 253        struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 254        int ret;
 255
 256        plat->spireg = (struct spi_reg *)devfdt_get_addr(bus);
 257
 258        ret = clk_get_by_index(bus, 0, &plat->clk);
 259        if (ret) {
 260                dev_err(bus, "cannot get clock\n");
 261                return ret;
 262        }
 263
 264        return 0;
 265}
 266
 267static int mvebu_spi_remove(struct udevice *bus)
 268{
 269        struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
 270
 271        clk_free(&plat->clk);
 272
 273        return 0;
 274}
 275
 276static const struct dm_spi_ops mvebu_spi_ops = {
 277        .xfer           = mvebu_spi_xfer,
 278        .set_speed      = mvebu_spi_set_speed,
 279        .set_mode       = mvebu_spi_set_mode,
 280        /*
 281         * cs_info is not needed, since we require all chip selects to be
 282         * in the device tree explicitly
 283         */
 284};
 285
 286static const struct udevice_id mvebu_spi_ids[] = {
 287        { .compatible = "marvell,armada-3700-spi" },
 288        { }
 289};
 290
 291U_BOOT_DRIVER(mvebu_spi) = {
 292        .name = "mvebu_spi",
 293        .id = UCLASS_SPI,
 294        .of_match = mvebu_spi_ids,
 295        .ops = &mvebu_spi_ops,
 296        .ofdata_to_platdata = mvebu_spi_ofdata_to_platdata,
 297        .platdata_auto_alloc_size = sizeof(struct mvebu_spi_platdata),
 298        .probe = mvebu_spi_probe,
 299        .remove = mvebu_spi_remove,
 300};
 301