uboot/drivers/spi/bcmstb_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2018  Cisco Systems, Inc.
   4 *
   5 * Author: Thomas Fitzsimmons <fitzsim@fitzsim.org>
   6 */
   7
   8#include <asm/io.h>
   9#include <command.h>
  10#include <config.h>
  11#include <dm.h>
  12#include <errno.h>
  13#include <fdtdec.h>
  14#include <linux/bitops.h>
  15#include <linux/delay.h>
  16#include <log.h>
  17#include <malloc.h>
  18#include <spi.h>
  19#include <time.h>
  20
  21DECLARE_GLOBAL_DATA_PTR;
  22
  23#define SPBR_MIN                8
  24#define BITS_PER_WORD           8
  25
  26#define NUM_TXRAM               32
  27#define NUM_RXRAM               32
  28#define NUM_CDRAM               16
  29
  30/* hif_mspi register structure. */
  31struct bcmstb_hif_mspi_regs {
  32        u32 spcr0_lsb;          /* 0x000 */
  33        u32 spcr0_msb;          /* 0x004 */
  34        u32 spcr1_lsb;          /* 0x008 */
  35        u32 spcr1_msb;          /* 0x00c */
  36        u32 newqp;              /* 0x010 */
  37        u32 endqp;              /* 0x014 */
  38        u32 spcr2;              /* 0x018 */
  39        u32 reserved0;          /* 0x01c */
  40        u32 mspi_status;        /* 0x020 */
  41        u32 cptqp;              /* 0x024 */
  42        u32 spcr3;              /* 0x028 */
  43        u32 revision;           /* 0x02c */
  44        u32 reserved1[4];       /* 0x030 */
  45        u32 txram[NUM_TXRAM];   /* 0x040 */
  46        u32 rxram[NUM_RXRAM];   /* 0x0c0 */
  47        u32 cdram[NUM_CDRAM];   /* 0x140 */
  48        u32 write_lock;         /* 0x180 */
  49};
  50
  51/* hif_mspi masks. */
  52#define HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK      0x00000080
  53#define HIF_MSPI_SPCR2_SPE_MASK                 0x00000040
  54#define HIF_MSPI_SPCR2_SPIFIE_MASK              0x00000020
  55#define HIF_MSPI_WRITE_LOCK_WRITE_LOCK_MASK     0x00000001
  56
  57/* bspi offsets. */
  58#define BSPI_MAST_N_BOOT_CTRL                   0x008
  59
  60/* bspi_raf is not used in this driver. */
  61
  62/* hif_spi_intr2 offsets and masks. */
  63#define HIF_SPI_INTR2_CPU_CLEAR                 0x08
  64#define HIF_SPI_INTR2_CPU_MASK_SET              0x10
  65#define HIF_SPI_INTR2_CPU_MASK_CLEAR            0x14
  66#define HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK    0x00000020
  67
  68/* SPI transfer timeout in milliseconds. */
  69#define HIF_MSPI_WAIT                           10
  70
  71enum bcmstb_base_type {
  72        HIF_MSPI,
  73        BSPI,
  74        HIF_SPI_INTR2,
  75        CS_REG,
  76        BASE_LAST,
  77};
  78
  79struct bcmstb_spi_platdata {
  80        void *base[4];
  81};
  82
  83struct bcmstb_spi_priv {
  84        struct bcmstb_hif_mspi_regs *regs;
  85        void *bspi;
  86        void *hif_spi_intr2;
  87        void *cs_reg;
  88        int default_cs;
  89        int curr_cs;
  90        uint tx_slot;
  91        uint rx_slot;
  92        u8 saved_cmd[NUM_CDRAM];
  93        uint saved_cmd_len;
  94        void *saved_din_addr;
  95};
  96
  97static int bcmstb_spi_ofdata_to_platdata(struct udevice *bus)
  98{
  99        struct bcmstb_spi_platdata *plat = dev_get_platdata(bus);
 100        const void *fdt = gd->fdt_blob;
 101        int node = dev_of_offset(bus);
 102        int ret = 0;
 103        int i = 0;
 104        struct fdt_resource resource = { 0 };
 105        char *names[BASE_LAST] = { "hif_mspi", "bspi", "hif_spi_intr2",
 106                                   "cs_reg" };
 107        const phys_addr_t defaults[BASE_LAST] = { BCMSTB_HIF_MSPI_BASE,
 108                                                  BCMSTB_BSPI_BASE,
 109                                                  BCMSTB_HIF_SPI_INTR2,
 110                                                  BCMSTB_CS_REG };
 111
 112        for (i = 0; i < BASE_LAST; i++) {
 113                plat->base[i] = (void *)defaults[i];
 114
 115                ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
 116                                             names[i], &resource);
 117                if (ret) {
 118                        printf("%s: Assuming BCMSTB SPI %s address 0x0x%p\n",
 119                               __func__, names[i], (void *)defaults[i]);
 120                } else {
 121                        plat->base[i] = (void *)resource.start;
 122                        debug("BCMSTB SPI %s address: 0x0x%p\n",
 123                              names[i], (void *)plat->base[i]);
 124                }
 125        }
 126
 127        return 0;
 128}
 129
 130static void bcmstb_spi_hw_set_parms(struct bcmstb_spi_priv *priv)
 131{
 132        writel(SPBR_MIN, &priv->regs->spcr0_lsb);
 133        writel(BITS_PER_WORD << 2 | SPI_MODE_3, &priv->regs->spcr0_msb);
 134}
 135
 136static void bcmstb_spi_enable_interrupt(void *base, u32 mask)
 137{
 138        void *reg = base + HIF_SPI_INTR2_CPU_MASK_CLEAR;
 139
 140        writel(readl(reg) | mask, reg);
 141        readl(reg);
 142}
 143
 144static void bcmstb_spi_disable_interrupt(void *base, u32 mask)
 145{
 146        void *reg = base + HIF_SPI_INTR2_CPU_MASK_SET;
 147
 148        writel(readl(reg) | mask, reg);
 149        readl(reg);
 150}
 151
 152static void bcmstb_spi_clear_interrupt(void *base, u32 mask)
 153{
 154        void *reg = base + HIF_SPI_INTR2_CPU_CLEAR;
 155
 156        writel(readl(reg) | mask, reg);
 157        readl(reg);
 158}
 159
 160static int bcmstb_spi_probe(struct udevice *bus)
 161{
 162        struct bcmstb_spi_platdata *plat = dev_get_platdata(bus);
 163        struct bcmstb_spi_priv *priv = dev_get_priv(bus);
 164
 165        priv->regs = plat->base[HIF_MSPI];
 166        priv->bspi = plat->base[BSPI];
 167        priv->hif_spi_intr2 = plat->base[HIF_SPI_INTR2];
 168        priv->cs_reg = plat->base[CS_REG];
 169        priv->default_cs = 0;
 170        priv->curr_cs = -1;
 171        priv->tx_slot = 0;
 172        priv->rx_slot = 0;
 173        memset(priv->saved_cmd, 0, NUM_CDRAM);
 174        priv->saved_cmd_len = 0;
 175        priv->saved_din_addr = NULL;
 176
 177        debug("spi_xfer: tx regs: 0x%p\n", &priv->regs->txram[0]);
 178        debug("spi_xfer: rx regs: 0x%p\n", &priv->regs->rxram[0]);
 179
 180        /* Disable BSPI. */
 181        writel(1, priv->bspi + BSPI_MAST_N_BOOT_CTRL);
 182        readl(priv->bspi + BSPI_MAST_N_BOOT_CTRL);
 183
 184        /* Set up interrupts. */
 185        bcmstb_spi_disable_interrupt(priv->hif_spi_intr2, 0xffffffff);
 186        bcmstb_spi_clear_interrupt(priv->hif_spi_intr2, 0xffffffff);
 187        bcmstb_spi_enable_interrupt(priv->hif_spi_intr2,
 188                                    HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK);
 189
 190        /* Set up control registers. */
 191        writel(0, &priv->regs->spcr1_lsb);
 192        writel(0, &priv->regs->spcr1_msb);
 193        writel(0, &priv->regs->newqp);
 194        writel(0, &priv->regs->endqp);
 195        writel(HIF_MSPI_SPCR2_SPIFIE_MASK, &priv->regs->spcr2);
 196        writel(0, &priv->regs->spcr3);
 197
 198        bcmstb_spi_hw_set_parms(priv);
 199
 200        return 0;
 201}
 202
 203static void bcmstb_spi_submit(struct bcmstb_spi_priv *priv, bool done)
 204{
 205        debug("WR NEWQP: %d\n", 0);
 206        writel(0, &priv->regs->newqp);
 207
 208        debug("WR ENDQP: %d\n", priv->tx_slot - 1);
 209        writel(priv->tx_slot - 1, &priv->regs->endqp);
 210
 211        if (done) {
 212                debug("WR CDRAM[%d]: %02x\n", priv->tx_slot - 1,
 213                      readl(&priv->regs->cdram[priv->tx_slot - 1]) & ~0x80);
 214                writel(readl(&priv->regs->cdram[priv->tx_slot - 1]) & ~0x80,
 215                       &priv->regs->cdram[priv->tx_slot - 1]);
 216        }
 217
 218        /* Force chip select first time. */
 219        if (priv->curr_cs != priv->default_cs) {
 220                debug("spi_xfer: switching chip select to %d\n",
 221                      priv->default_cs);
 222                writel((readl(priv->cs_reg) & ~0xff) | (1 << priv->default_cs),
 223                       priv->cs_reg);
 224                readl(priv->cs_reg);
 225                udelay(10);
 226                priv->curr_cs = priv->default_cs;
 227        }
 228
 229        debug("WR WRITE_LOCK: %02x\n", 1);
 230        writel((readl(&priv->regs->write_lock) &
 231                ~HIF_MSPI_WRITE_LOCK_WRITE_LOCK_MASK) | 1,
 232               &priv->regs->write_lock);
 233        readl(&priv->regs->write_lock);
 234
 235        debug("WR SPCR2: %02x\n",
 236              HIF_MSPI_SPCR2_SPIFIE_MASK |
 237              HIF_MSPI_SPCR2_SPE_MASK |
 238              HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK);
 239        writel(HIF_MSPI_SPCR2_SPIFIE_MASK |
 240               HIF_MSPI_SPCR2_SPE_MASK |
 241               HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK,
 242               &priv->regs->spcr2);
 243}
 244
 245static int bcmstb_spi_wait(struct bcmstb_spi_priv *priv)
 246{
 247        u32 start_time = get_timer(0);
 248        u32 status = readl(&priv->regs->mspi_status);
 249
 250        while (!(status & 1)) {
 251                if (get_timer(start_time) > HIF_MSPI_WAIT)
 252                        return -ETIMEDOUT;
 253                status = readl(&priv->regs->mspi_status);
 254        }
 255
 256        writel(readl(&priv->regs->mspi_status) & ~1, &priv->regs->mspi_status);
 257        bcmstb_spi_clear_interrupt(priv->hif_spi_intr2,
 258                                   HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK);
 259
 260        return 0;
 261}
 262
 263static int bcmstb_spi_xfer(struct udevice *dev, unsigned int bitlen,
 264                           const void *dout, void *din, unsigned long flags)
 265{
 266        uint len = bitlen / 8;
 267        uint tx_len = len;
 268        uint rx_len = len;
 269        const u8 *out_bytes = (u8 *)dout;
 270        u8 *in_bytes = (u8 *)din;
 271        struct udevice *bus = dev_get_parent(dev);
 272        struct bcmstb_spi_priv *priv = dev_get_priv(bus);
 273        struct bcmstb_hif_mspi_regs *regs = priv->regs;
 274
 275        debug("spi_xfer: %d, t: 0x%p, r: 0x%p, f: %lx\n",
 276              len, dout, din, flags);
 277        debug("spi_xfer: chip select: %x\n", readl(priv->cs_reg) & 0xff);
 278        debug("spi_xfer: tx addr: 0x%p\n", &regs->txram[0]);
 279        debug("spi_xfer: rx addr: 0x%p\n", &regs->rxram[0]);
 280        debug("spi_xfer: cd addr: 0x%p\n", &regs->cdram[0]);
 281
 282        if (flags & SPI_XFER_END) {
 283                debug("spi_xfer: clearing saved din address: 0x%p\n",
 284                      priv->saved_din_addr);
 285                priv->saved_din_addr = NULL;
 286                priv->saved_cmd_len = 0;
 287                memset(priv->saved_cmd, 0, NUM_CDRAM);
 288        }
 289
 290        if (bitlen == 0)
 291                return 0;
 292
 293        if (bitlen % 8) {
 294                printf("%s: Non-byte-aligned transfer\n", __func__);
 295                return -EOPNOTSUPP;
 296        }
 297
 298        if (flags & ~(SPI_XFER_BEGIN | SPI_XFER_END)) {
 299                printf("%s: Unsupported flags: %lx\n", __func__, flags);
 300                return -EOPNOTSUPP;
 301        }
 302
 303        if (flags & SPI_XFER_BEGIN) {
 304                priv->tx_slot = 0;
 305                priv->rx_slot = 0;
 306
 307                if (out_bytes && len > NUM_CDRAM) {
 308                        printf("%s: Unable to save transfer\n", __func__);
 309                        return -EOPNOTSUPP;
 310                }
 311
 312                if (out_bytes && !(flags & SPI_XFER_END)) {
 313                        /*
 314                         * This is the start of a transmit operation
 315                         * that will need repeating if the calling
 316                         * code polls for the result.  Save it for
 317                         * subsequent transmission.
 318                         */
 319                        debug("spi_xfer: saving command: %x, %d\n",
 320                              out_bytes[0], len);
 321                        priv->saved_cmd_len = len;
 322                        memcpy(priv->saved_cmd, out_bytes, priv->saved_cmd_len);
 323                }
 324        }
 325
 326        if (!(flags & (SPI_XFER_BEGIN | SPI_XFER_END))) {
 327                if (priv->saved_din_addr == din) {
 328                        /*
 329                         * The caller is polling for status.  Repeat
 330                         * the last transmission.
 331                         */
 332                        int ret = 0;
 333
 334                        debug("spi_xfer: Making recursive call\n");
 335                        ret = bcmstb_spi_xfer(dev, priv->saved_cmd_len * 8,
 336                                              priv->saved_cmd, NULL,
 337                                              SPI_XFER_BEGIN);
 338                        if (ret) {
 339                                printf("%s: Recursive call failed\n", __func__);
 340                                return ret;
 341                        }
 342                } else {
 343                        debug("spi_xfer: saving din address: 0x%p\n", din);
 344                        priv->saved_din_addr = din;
 345                }
 346        }
 347
 348        while (rx_len > 0) {
 349                priv->rx_slot = priv->tx_slot;
 350
 351                while (priv->tx_slot < NUM_CDRAM && tx_len > 0) {
 352                        bcmstb_spi_hw_set_parms(priv);
 353                        debug("WR TXRAM[%d]: %02x\n", priv->tx_slot,
 354                              out_bytes ? out_bytes[len - tx_len] : 0xff);
 355                        writel(out_bytes ? out_bytes[len - tx_len] : 0xff,
 356                               &regs->txram[priv->tx_slot << 1]);
 357                        debug("WR CDRAM[%d]: %02x\n", priv->tx_slot, 0x8e);
 358                        writel(0x8e, &regs->cdram[priv->tx_slot]);
 359                        priv->tx_slot++;
 360                        tx_len--;
 361                        if (!in_bytes)
 362                                rx_len--;
 363                }
 364
 365                debug("spi_xfer: early return clauses: %d, %d, %d\n",
 366                      len <= NUM_CDRAM,
 367                      !in_bytes,
 368                      (flags & (SPI_XFER_BEGIN |
 369                                SPI_XFER_END)) == SPI_XFER_BEGIN);
 370                if (len <= NUM_CDRAM &&
 371                    !in_bytes &&
 372                    (flags & (SPI_XFER_BEGIN | SPI_XFER_END)) == SPI_XFER_BEGIN)
 373                        return 0;
 374
 375                bcmstb_spi_submit(priv, tx_len == 0);
 376
 377                if (bcmstb_spi_wait(priv) == -ETIMEDOUT) {
 378                        printf("%s: Timed out\n", __func__);
 379                        return -ETIMEDOUT;
 380                }
 381
 382                priv->tx_slot %= NUM_CDRAM;
 383
 384                if (in_bytes) {
 385                        while (priv->rx_slot < NUM_CDRAM && rx_len > 0) {
 386                                in_bytes[len - rx_len] =
 387                                        readl(&regs->rxram[(priv->rx_slot << 1)
 388                                                           + 1])
 389                                        & 0xff;
 390                                debug("RD RXRAM[%d]: %02x\n",
 391                                      priv->rx_slot, in_bytes[len - rx_len]);
 392                                priv->rx_slot++;
 393                                rx_len--;
 394                        }
 395                }
 396        }
 397
 398        if (flags & SPI_XFER_END) {
 399                debug("WR WRITE_LOCK: %02x\n", 0);
 400                writel((readl(&priv->regs->write_lock) &
 401                        ~HIF_MSPI_WRITE_LOCK_WRITE_LOCK_MASK) | 0,
 402                       &priv->regs->write_lock);
 403                readl(&priv->regs->write_lock);
 404        }
 405
 406        return 0;
 407}
 408
 409static int bcmstb_spi_set_speed(struct udevice *dev, uint speed)
 410{
 411        return 0;
 412}
 413
 414static int bcmstb_spi_set_mode(struct udevice *dev, uint mode)
 415{
 416        return 0;
 417}
 418
 419static const struct dm_spi_ops bcmstb_spi_ops = {
 420        .xfer           = bcmstb_spi_xfer,
 421        .set_speed      = bcmstb_spi_set_speed,
 422        .set_mode       = bcmstb_spi_set_mode,
 423};
 424
 425static const struct udevice_id bcmstb_spi_id[] = {
 426        { .compatible = "brcm,spi-brcmstb" },
 427        { }
 428};
 429
 430U_BOOT_DRIVER(bcmstb_spi) = {
 431        .name                           = "bcmstb_spi",
 432        .id                             = UCLASS_SPI,
 433        .of_match                       = bcmstb_spi_id,
 434        .ops                            = &bcmstb_spi_ops,
 435        .ofdata_to_platdata             = bcmstb_spi_ofdata_to_platdata,
 436        .probe                          = bcmstb_spi_probe,
 437        .platdata_auto_alloc_size       = sizeof(struct bcmstb_spi_platdata),
 438        .priv_auto_alloc_size           = sizeof(struct bcmstb_spi_priv),
 439};
 440