uboot/drivers/spi/spi-synquacer.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * spi-synquacer.c - Socionext Synquacer SPI driver
   4 * Copyright 2021 Linaro Ltd.
   5 * Copyright 2021 Socionext, Inc.
   6 */
   7
   8#include <clk.h>
   9#include <common.h>
  10#include <dm.h>
  11#include <log.h>
  12#include <time.h>
  13#include <dm/device_compat.h>
  14#include <linux/bitfield.h>
  15#include <linux/bitops.h>
  16#include <linux/delay.h>
  17#include <linux/io.h>
  18#include <spi.h>
  19#include <wait_bit.h>
  20
  21#define MCTRL   0x0
  22#define MEN     0
  23#define CSEN    1
  24#define IPCLK   3
  25#define MES     4
  26#define SYNCON  5
  27
  28#define PCC0            0x4
  29#define PCC(n)          (PCC0 + (n) * 4)
  30#define RTM             3
  31#define ACES            2
  32#define SAFESYNC        16
  33#define CPHA            0
  34#define CPOL            1
  35#define SSPOL           4
  36#define SDIR            7
  37#define SS2CD           5
  38#define SENDIAN         8
  39#define CDRS_SHIFT      9
  40#define CDRS_MASK       0x7f
  41
  42#define TXF             0x14
  43#define TXE             0x18
  44#define TXC             0x1c
  45#define RXF             0x20
  46#define RXE             0x24
  47#define RXC             0x28
  48#define TFLETE          4
  49#define RFMTE           5
  50
  51#define FAULTF          0x2c
  52#define FAULTC          0x30
  53
  54#define DMCFG           0x34
  55#define SSDC            1
  56#define MSTARTEN        2
  57
  58#define DMSTART         0x38
  59#define TRIGGER         0
  60#define DMSTOP          8
  61#define CS_MASK         3
  62#define CS_SHIFT        16
  63#define DATA_TXRX       0
  64#define DATA_RX         1
  65#define DATA_TX         2
  66#define DATA_MASK       3
  67#define DATA_SHIFT      26
  68#define BUS_WIDTH       24
  69
  70#define DMBCC           0x3c
  71#define DMSTATUS        0x40
  72#define RX_DATA_MASK    0x1f
  73#define RX_DATA_SHIFT   8
  74#define TX_DATA_MASK    0x1f
  75#define TX_DATA_SHIFT   16
  76
  77#define TXBITCNT        0x44
  78
  79#define FIFOCFG         0x4c
  80#define BPW_MASK        0x3
  81#define BPW_SHIFT       8
  82#define RX_FLUSH        11
  83#define TX_FLUSH        12
  84#define RX_TRSHLD_MASK          0xf
  85#define RX_TRSHLD_SHIFT         0
  86#define TX_TRSHLD_MASK          0xf
  87#define TX_TRSHLD_SHIFT         4
  88
  89#define TXFIFO          0x50
  90#define RXFIFO          0x90
  91#define MID             0xfc
  92
  93#define FIFO_DEPTH      16
  94#define TX_TRSHLD       4
  95#define RX_TRSHLD       (FIFO_DEPTH - TX_TRSHLD)
  96
  97#define TXBIT   1
  98#define RXBIT   2
  99
 100DECLARE_GLOBAL_DATA_PTR;
 101
 102struct synquacer_spi_plat {
 103        void __iomem *base;
 104        bool aces, rtm;
 105};
 106
 107struct synquacer_spi_priv {
 108        void __iomem *base;
 109        bool aces, rtm;
 110        int speed, cs, mode, rwflag;
 111        void *rx_buf;
 112        const void *tx_buf;
 113        unsigned int tx_words, rx_words;
 114};
 115
 116static void read_fifo(struct synquacer_spi_priv *priv)
 117{
 118        u32 len = readl(priv->base + DMSTATUS);
 119        u8 *buf = priv->rx_buf;
 120        int i;
 121
 122        len = (len >> RX_DATA_SHIFT) & RX_DATA_MASK;
 123        len = min_t(unsigned int, len, priv->rx_words);
 124
 125        for (i = 0; i < len; i++)
 126                *buf++ = readb(priv->base + RXFIFO);
 127
 128        priv->rx_buf = buf;
 129        priv->rx_words -= len;
 130}
 131
 132static void write_fifo(struct synquacer_spi_priv *priv)
 133{
 134        u32 len = readl(priv->base + DMSTATUS);
 135        const u8 *buf = priv->tx_buf;
 136        int i;
 137
 138        len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK;
 139        len = min_t(unsigned int, FIFO_DEPTH - len, priv->tx_words);
 140
 141        for (i = 0; i < len; i++)
 142                writeb(*buf++, priv->base + TXFIFO);
 143
 144        priv->tx_buf = buf;
 145        priv->tx_words -= len;
 146}
 147
 148static void synquacer_cs_set(struct synquacer_spi_priv *priv, bool active)
 149{
 150        u32 val;
 151
 152        val = readl(priv->base + DMSTART);
 153        val &= ~(CS_MASK << CS_SHIFT);
 154        val |= priv->cs << CS_SHIFT;
 155
 156        if (active) {
 157                writel(val, priv->base + DMSTART);
 158
 159                val = readl(priv->base + DMSTART);
 160                val &= ~BIT(DMSTOP);
 161                writel(val, priv->base + DMSTART);
 162        } else {
 163                val |= BIT(DMSTOP);
 164                writel(val, priv->base + DMSTART);
 165
 166                if (priv->rx_buf) {
 167                        u32 buf[16];
 168
 169                        priv->rx_buf = buf;
 170                        priv->rx_words = 16;
 171                        read_fifo(priv);
 172                }
 173        }
 174}
 175
 176static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
 177{
 178        struct udevice *bus = dev->parent;
 179        struct synquacer_spi_priv *priv = dev_get_priv(bus);
 180        struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
 181        u32 val, div, bus_width;
 182        int rwflag;
 183
 184        rwflag = (rx ? 1 : 0) | (tx ? 2 : 0);
 185
 186        /* if nothing to do */
 187        if (slave_plat->mode == priv->mode &&
 188            rwflag == priv->rwflag &&
 189            slave_plat->cs == priv->cs &&
 190            slave_plat->max_hz == priv->speed)
 191                return;
 192
 193        priv->rwflag = rwflag;
 194        priv->cs = slave_plat->cs;
 195        priv->mode = slave_plat->mode;
 196        priv->speed = slave_plat->max_hz;
 197
 198        if (priv->mode & SPI_TX_BYTE)
 199                bus_width = 1;
 200        else if (priv->mode & SPI_TX_DUAL)
 201                bus_width = 2;
 202        else if (priv->mode & SPI_TX_QUAD)
 203                bus_width = 4;
 204        else if (priv->mode & SPI_TX_OCTAL)
 205                bus_width = 8;
 206
 207        div = DIV_ROUND_UP(125000000, priv->speed);
 208
 209        val = readl(priv->base + PCC(priv->cs));
 210        val &= ~BIT(RTM);
 211        val &= ~BIT(ACES);
 212        val &= ~BIT(SAFESYNC);
 213        if ((priv->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3)
 214                val |= BIT(SAFESYNC);
 215        if ((priv->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6)
 216                val |= BIT(SAFESYNC);
 217
 218        if (priv->mode & SPI_CPHA)
 219                val |= BIT(CPHA);
 220        else
 221                val &= ~BIT(CPHA);
 222
 223        if (priv->mode & SPI_CPOL)
 224                val |= BIT(CPOL);
 225        else
 226                val &= ~BIT(CPOL);
 227
 228        if (priv->mode & SPI_CS_HIGH)
 229                val |= BIT(SSPOL);
 230        else
 231                val &= ~BIT(SSPOL);
 232
 233        if (priv->mode & SPI_LSB_FIRST)
 234                val |= BIT(SDIR);
 235        else
 236                val &= ~BIT(SDIR);
 237
 238        if (priv->aces)
 239                val |= BIT(ACES);
 240
 241        if (priv->rtm)
 242                val |= BIT(RTM);
 243
 244        val |= (3 << SS2CD);
 245        val |= BIT(SENDIAN);
 246
 247        val &= ~(CDRS_MASK << CDRS_SHIFT);
 248        val |= ((div >> 1) << CDRS_SHIFT);
 249
 250        writel(val, priv->base + PCC(priv->cs));
 251
 252        val = readl(priv->base + FIFOCFG);
 253        val &= ~(BPW_MASK << BPW_SHIFT);
 254        val |= (0 << BPW_SHIFT);
 255        writel(val, priv->base + FIFOCFG);
 256
 257        val = readl(priv->base + DMSTART);
 258        val &= ~(DATA_MASK << DATA_SHIFT);
 259
 260        if (tx && rx)
 261                val |= (DATA_TXRX << DATA_SHIFT);
 262        else if (rx)
 263                val |= (DATA_RX << DATA_SHIFT);
 264        else
 265                val |= (DATA_TX << DATA_SHIFT);
 266
 267        val &= ~(3 << BUS_WIDTH);
 268        val |= ((bus_width >> 1) << BUS_WIDTH);
 269        writel(val, priv->base + DMSTART);
 270}
 271
 272static int synquacer_spi_xfer(struct udevice *dev, unsigned int bitlen,
 273                              const void *tx_buf, void *rx_buf,
 274                              unsigned long flags)
 275{
 276        struct udevice *bus = dev->parent;
 277        struct synquacer_spi_priv *priv = dev_get_priv(bus);
 278        u32 val, words, busy;
 279
 280        val = readl(priv->base + FIFOCFG);
 281        val |= (1 << RX_FLUSH);
 282        val |= (1 << TX_FLUSH);
 283        writel(val, priv->base + FIFOCFG);
 284
 285        synquacer_spi_config(dev, rx_buf, tx_buf);
 286
 287        priv->tx_buf = tx_buf;
 288        priv->rx_buf = rx_buf;
 289
 290        words = bitlen / 8;
 291
 292        if (tx_buf) {
 293                busy |= BIT(TXBIT);
 294                priv->tx_words = words;
 295        } else {
 296                busy &= ~BIT(TXBIT);
 297                priv->tx_words = 0;
 298        }
 299
 300        if (rx_buf) {
 301                busy |= BIT(RXBIT);
 302                priv->rx_words = words;
 303        } else {
 304                busy &= ~BIT(RXBIT);
 305                priv->rx_words = 0;
 306        }
 307
 308        if (flags & SPI_XFER_BEGIN)
 309                synquacer_cs_set(priv, true);
 310
 311        if (tx_buf)
 312                write_fifo(priv);
 313
 314        if (rx_buf) {
 315                val = readl(priv->base + FIFOCFG);
 316                val &= ~(RX_TRSHLD_MASK << RX_TRSHLD_SHIFT);
 317                val |= ((priv->rx_words > FIFO_DEPTH ?
 318                        RX_TRSHLD : priv->rx_words) << RX_TRSHLD_SHIFT);
 319                writel(val, priv->base + FIFOCFG);
 320        }
 321
 322        writel(~0, priv->base + TXC);
 323        writel(~0, priv->base + RXC);
 324
 325        /* Trigger */
 326        val = readl(priv->base + DMSTART);
 327        val |= BIT(TRIGGER);
 328        writel(val, priv->base + DMSTART);
 329
 330        while (busy & (BIT(RXBIT) | BIT(TXBIT))) {
 331                if (priv->rx_words)
 332                        read_fifo(priv);
 333                else
 334                        busy &= ~BIT(RXBIT);
 335
 336                if (priv->tx_words) {
 337                        write_fifo(priv);
 338                } else {
 339                        u32 len;
 340
 341                        do { /* wait for shifter to empty out */
 342                                cpu_relax();
 343                                len = readl(priv->base + DMSTATUS);
 344                                len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK;
 345                        } while (tx_buf && len);
 346                        busy &= ~BIT(TXBIT);
 347                }
 348        }
 349
 350        if (flags & SPI_XFER_END)
 351                synquacer_cs_set(priv, false);
 352
 353        return 0;
 354}
 355
 356static int synquacer_spi_set_speed(struct udevice *bus, uint speed)
 357{
 358        return 0;
 359}
 360
 361static int synquacer_spi_set_mode(struct udevice *bus, uint mode)
 362{
 363        return 0;
 364}
 365
 366static int synquacer_spi_claim_bus(struct udevice *dev)
 367{
 368        return 0;
 369}
 370
 371static int synquacer_spi_release_bus(struct udevice *dev)
 372{
 373        return 0;
 374}
 375
 376static void synquacer_spi_disable_module(struct synquacer_spi_priv *priv)
 377{
 378        writel(0, priv->base + MCTRL);
 379        while (readl(priv->base + MCTRL) & BIT(MES))
 380                cpu_relax();
 381}
 382
 383static void synquacer_spi_init(struct synquacer_spi_priv *priv)
 384{
 385        u32 val;
 386
 387        synquacer_spi_disable_module(priv);
 388
 389        writel(0, priv->base + TXE);
 390        writel(0, priv->base + RXE);
 391        val = readl(priv->base + TXF);
 392        writel(val, priv->base + TXC);
 393        val = readl(priv->base + RXF);
 394        writel(val, priv->base + RXC);
 395        val = readl(priv->base + FAULTF);
 396        writel(val, priv->base + FAULTC);
 397
 398        val = readl(priv->base + DMCFG);
 399        val &= ~BIT(SSDC);
 400        val &= ~BIT(MSTARTEN);
 401        writel(val, priv->base + DMCFG);
 402
 403        /* Enable module with direct mode */
 404        val = readl(priv->base + MCTRL);
 405        val &= ~BIT(IPCLK);
 406        val &= ~BIT(CSEN);
 407        val |= BIT(MEN);
 408        val |= BIT(SYNCON);
 409        writel(val, priv->base + MCTRL);
 410}
 411
 412static void synquacer_spi_exit(struct synquacer_spi_priv *priv)
 413{
 414        u32 val;
 415
 416        synquacer_spi_disable_module(priv);
 417
 418        /* Enable module with command sequence mode */
 419        val = readl(priv->base + MCTRL);
 420        val &= ~BIT(IPCLK);
 421        val |= BIT(CSEN);
 422        val |= BIT(MEN);
 423        val |= BIT(SYNCON);
 424        writel(val, priv->base + MCTRL);
 425
 426        while (!(readl(priv->base + MCTRL) & BIT(MES)))
 427                cpu_relax();
 428}
 429
 430static int synquacer_spi_probe(struct udevice *bus)
 431{
 432        struct synquacer_spi_plat *plat = dev_get_plat(bus);
 433        struct synquacer_spi_priv *priv = dev_get_priv(bus);
 434
 435        priv->base = plat->base;
 436        priv->aces = plat->aces;
 437        priv->rtm = plat->rtm;
 438
 439        synquacer_spi_init(priv);
 440        return 0;
 441}
 442
 443static int synquacer_spi_remove(struct udevice *bus)
 444{
 445        struct synquacer_spi_priv *priv = dev_get_priv(bus);
 446
 447        synquacer_spi_exit(priv);
 448        return 0;
 449}
 450
 451static int synquacer_spi_of_to_plat(struct udevice *bus)
 452{
 453        struct synquacer_spi_plat *plat = dev_get_plat(bus);
 454        struct clk clk;
 455
 456        plat->base = dev_read_addr_ptr(bus);
 457
 458        plat->aces = dev_read_bool(bus, "socionext,set-aces");
 459        plat->rtm = dev_read_bool(bus, "socionext,use-rtm");
 460
 461        clk_get_by_name(bus, "iHCLK", &clk);
 462        clk_enable(&clk);
 463
 464        return 0;
 465}
 466
 467static const struct dm_spi_ops synquacer_spi_ops = {
 468        .claim_bus      = synquacer_spi_claim_bus,
 469        .release_bus    = synquacer_spi_release_bus,
 470        .xfer           = synquacer_spi_xfer,
 471        .set_speed      = synquacer_spi_set_speed,
 472        .set_mode       = synquacer_spi_set_mode,
 473};
 474
 475static const struct udevice_id synquacer_spi_ids[] = {
 476        { .compatible = "socionext,synquacer-spi" },
 477        { /* Sentinel */ }
 478};
 479
 480U_BOOT_DRIVER(synquacer_spi) = {
 481        .name           = "synquacer_spi",
 482        .id             = UCLASS_SPI,
 483        .of_match       = synquacer_spi_ids,
 484        .ops            = &synquacer_spi_ops,
 485        .of_to_plat     = synquacer_spi_of_to_plat,
 486        .plat_auto      = sizeof(struct synquacer_spi_plat),
 487        .priv_auto      = sizeof(struct synquacer_spi_priv),
 488        .probe          = synquacer_spi_probe,
 489        .flags          = DM_FLAG_OS_PREPARE,
 490        .remove         = synquacer_spi_remove,
 491};
 492