linux/drivers/spi/spi-bcm53xx.c
<<
>>
Prefs
   1#define pr_fmt(fmt)             KBUILD_MODNAME ": " fmt
   2
   3#include <linux/kernel.h>
   4#include <linux/module.h>
   5#include <linux/slab.h>
   6#include <linux/delay.h>
   7#include <linux/bcma/bcma.h>
   8#include <linux/spi/spi.h>
   9
  10#include "spi-bcm53xx.h"
  11
  12#define BCM53XXSPI_MAX_SPI_BAUD 13500000        /* 216 MHz? */
  13
  14/* The longest observed required wait was 19 ms */
  15#define BCM53XXSPI_SPE_TIMEOUT_MS       80
  16
  17struct bcm53xxspi {
  18        struct bcma_device *core;
  19        struct spi_master *master;
  20
  21        size_t read_offset;
  22};
  23
  24static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset)
  25{
  26        return bcma_read32(b53spi->core, offset);
  27}
  28
  29static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset,
  30                                    u32 value)
  31{
  32        bcma_write32(b53spi->core, offset, value);
  33}
  34
  35static inline unsigned int bcm53xxspi_calc_timeout(size_t len)
  36{
  37        /* Do some magic calculation based on length and buad. Add 10% and 1. */
  38        return (len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100) + 1;
  39}
  40
  41static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
  42{
  43        unsigned long deadline;
  44        u32 tmp;
  45
  46        /* SPE bit has to be 0 before we read MSPI STATUS */
  47        deadline = jiffies + msecs_to_jiffies(BCM53XXSPI_SPE_TIMEOUT_MS);
  48        do {
  49                tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
  50                if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
  51                        break;
  52                udelay(5);
  53        } while (!time_after_eq(jiffies, deadline));
  54
  55        if (tmp & B53SPI_MSPI_SPCR2_SPE)
  56                goto spi_timeout;
  57
  58        /* Check status */
  59        deadline = jiffies + msecs_to_jiffies(timeout_ms);
  60        do {
  61                tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
  62                if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
  63                        bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
  64                        return 0;
  65                }
  66
  67                cpu_relax();
  68                udelay(100);
  69        } while (!time_after_eq(jiffies, deadline));
  70
  71spi_timeout:
  72        bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
  73
  74        pr_err("Timeout waiting for SPI to be ready!\n");
  75
  76        return -EBUSY;
  77}
  78
  79static void bcm53xxspi_buf_write(struct bcm53xxspi *b53spi, u8 *w_buf,
  80                                 size_t len, bool cont)
  81{
  82        u32 tmp;
  83        int i;
  84
  85        for (i = 0; i < len; i++) {
  86                /* Transmit Register File MSB */
  87                bcm53xxspi_write(b53spi, B53SPI_MSPI_TXRAM + 4 * (i * 2),
  88                                 (unsigned int)w_buf[i]);
  89        }
  90
  91        for (i = 0; i < len; i++) {
  92                tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
  93                      B53SPI_CDRAM_PCS_DSCK;
  94                if (!cont && i == len - 1)
  95                        tmp &= ~B53SPI_CDRAM_CONT;
  96                tmp &= ~0x1;
  97                /* Command Register File */
  98                bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
  99        }
 100
 101        /* Set queue pointers */
 102        bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
 103        bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1);
 104
 105        if (cont)
 106                bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
 107
 108        /* Start SPI transfer */
 109        tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
 110        tmp |= B53SPI_MSPI_SPCR2_SPE;
 111        if (cont)
 112                tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
 113        bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
 114
 115        /* Wait for SPI to finish */
 116        bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
 117
 118        if (!cont)
 119                bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
 120
 121        b53spi->read_offset = len;
 122}
 123
 124static void bcm53xxspi_buf_read(struct bcm53xxspi *b53spi, u8 *r_buf,
 125                                size_t len, bool cont)
 126{
 127        u32 tmp;
 128        int i;
 129
 130        for (i = 0; i < b53spi->read_offset + len; i++) {
 131                tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
 132                      B53SPI_CDRAM_PCS_DSCK;
 133                if (!cont && i == b53spi->read_offset + len - 1)
 134                        tmp &= ~B53SPI_CDRAM_CONT;
 135                tmp &= ~0x1;
 136                /* Command Register File */
 137                bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
 138        }
 139
 140        /* Set queue pointers */
 141        bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
 142        bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP,
 143                         b53spi->read_offset + len - 1);
 144
 145        if (cont)
 146                bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
 147
 148        /* Start SPI transfer */
 149        tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
 150        tmp |= B53SPI_MSPI_SPCR2_SPE;
 151        if (cont)
 152                tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
 153        bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
 154
 155        /* Wait for SPI to finish */
 156        bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
 157
 158        if (!cont)
 159                bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
 160
 161        for (i = 0; i < len; ++i) {
 162                int offset = b53spi->read_offset + i;
 163
 164                /* Data stored in the transmit register file LSB */
 165                r_buf[i] = (u8)bcm53xxspi_read(b53spi, B53SPI_MSPI_RXRAM + 4 * (1 + offset * 2));
 166        }
 167
 168        b53spi->read_offset = 0;
 169}
 170
 171static int bcm53xxspi_transfer_one(struct spi_master *master,
 172                                   struct spi_device *spi,
 173                                   struct spi_transfer *t)
 174{
 175        struct bcm53xxspi *b53spi = spi_master_get_devdata(master);
 176        u8 *buf;
 177        size_t left;
 178
 179        if (t->tx_buf) {
 180                buf = (u8 *)t->tx_buf;
 181                left = t->len;
 182                while (left) {
 183                        size_t to_write = min_t(size_t, 16, left);
 184                        bool cont = left - to_write > 0;
 185
 186                        bcm53xxspi_buf_write(b53spi, buf, to_write, cont);
 187                        left -= to_write;
 188                        buf += to_write;
 189                }
 190        }
 191
 192        if (t->rx_buf) {
 193                buf = (u8 *)t->rx_buf;
 194                left = t->len;
 195                while (left) {
 196                        size_t to_read = min_t(size_t, 16 - b53spi->read_offset,
 197                                               left);
 198                        bool cont = left - to_read > 0;
 199
 200                        bcm53xxspi_buf_read(b53spi, buf, to_read, cont);
 201                        left -= to_read;
 202                        buf += to_read;
 203                }
 204        }
 205
 206        return 0;
 207}
 208
 209/**************************************************
 210 * BCMA
 211 **************************************************/
 212
 213static struct spi_board_info bcm53xx_info = {
 214        .modalias       = "bcm53xxspiflash",
 215};
 216
 217static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = {
 218        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS),
 219        {},
 220};
 221MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl);
 222
 223static int bcm53xxspi_bcma_probe(struct bcma_device *core)
 224{
 225        struct bcm53xxspi *b53spi;
 226        struct spi_master *master;
 227        int err;
 228
 229        if (core->bus->drv_cc.core->id.rev != 42) {
 230                pr_err("SPI on SoC with unsupported ChipCommon rev\n");
 231                return -ENOTSUPP;
 232        }
 233
 234        master = spi_alloc_master(&core->dev, sizeof(*b53spi));
 235        if (!master)
 236                return -ENOMEM;
 237
 238        b53spi = spi_master_get_devdata(master);
 239        b53spi->master = master;
 240        b53spi->core = core;
 241
 242        master->transfer_one = bcm53xxspi_transfer_one;
 243
 244        bcma_set_drvdata(core, b53spi);
 245
 246        err = devm_spi_register_master(&core->dev, master);
 247        if (err) {
 248                spi_master_put(master);
 249                bcma_set_drvdata(core, NULL);
 250                goto out;
 251        }
 252
 253        /* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */
 254        spi_new_device(master, &bcm53xx_info);
 255
 256out:
 257        return err;
 258}
 259
 260static void bcm53xxspi_bcma_remove(struct bcma_device *core)
 261{
 262        struct bcm53xxspi *b53spi = bcma_get_drvdata(core);
 263
 264        spi_unregister_master(b53spi->master);
 265}
 266
 267static struct bcma_driver bcm53xxspi_bcma_driver = {
 268        .name           = KBUILD_MODNAME,
 269        .id_table       = bcm53xxspi_bcma_tbl,
 270        .probe          = bcm53xxspi_bcma_probe,
 271        .remove         = bcm53xxspi_bcma_remove,
 272};
 273
 274/**************************************************
 275 * Init & exit
 276 **************************************************/
 277
 278static int __init bcm53xxspi_module_init(void)
 279{
 280        int err = 0;
 281
 282        err = bcma_driver_register(&bcm53xxspi_bcma_driver);
 283        if (err)
 284                pr_err("Failed to register bcma driver: %d\n", err);
 285
 286        return err;
 287}
 288
 289static void __exit bcm53xxspi_module_exit(void)
 290{
 291        bcma_driver_unregister(&bcm53xxspi_bcma_driver);
 292}
 293
 294module_init(bcm53xxspi_module_init);
 295module_exit(bcm53xxspi_module_exit);
 296
 297MODULE_DESCRIPTION("Broadcom BCM53xx SPI Controller driver");
 298MODULE_AUTHOR("Rafał Miłecki <zajec5@gmail.com>");
 299MODULE_LICENSE("GPL");
 300