123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * spi-synquacer.c - Socionext Synquacer SPI driver
- * Copyright 2021 Linaro Ltd.
- * Copyright 2021 Socionext, Inc.
- */
- #include <clk.h>
- #include <common.h>
- #include <dm.h>
- #include <log.h>
- #include <time.h>
- #include <dm/device_compat.h>
- #include <linux/bitfield.h>
- #include <linux/bitops.h>
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <spi.h>
- #include <wait_bit.h>
- #define MCTRL 0x0
- #define MEN 0
- #define CSEN 1
- #define IPCLK 3
- #define MES 4
- #define SYNCON 5
- #define PCC0 0x4
- #define PCC(n) (PCC0 + (n) * 4)
- #define RTM 3
- #define ACES 2
- #define SAFESYNC 16
- #define CPHA 0
- #define CPOL 1
- #define SSPOL 4
- #define SDIR 7
- #define SS2CD 5
- #define SENDIAN 8
- #define CDRS_SHIFT 9
- #define CDRS_MASK 0x7f
- #define TXF 0x14
- #define TXE 0x18
- #define TXC 0x1c
- #define RXF 0x20
- #define RXE 0x24
- #define RXC 0x28
- #define TFLETE 4
- #define RFMTE 5
- #define FAULTF 0x2c
- #define FAULTC 0x30
- #define DMCFG 0x34
- #define SSDC 1
- #define MSTARTEN 2
- #define DMSTART 0x38
- #define TRIGGER 0
- #define DMSTOP 8
- #define CS_MASK 3
- #define CS_SHIFT 16
- #define DATA_TXRX 0
- #define DATA_RX 1
- #define DATA_TX 2
- #define DATA_MASK 3
- #define DATA_SHIFT 26
- #define BUS_WIDTH 24
- #define DMBCC 0x3c
- #define DMSTATUS 0x40
- #define RX_DATA_MASK 0x1f
- #define RX_DATA_SHIFT 8
- #define TX_DATA_MASK 0x1f
- #define TX_DATA_SHIFT 16
- #define TXBITCNT 0x44
- #define FIFOCFG 0x4c
- #define BPW_MASK 0x3
- #define BPW_SHIFT 8
- #define RX_FLUSH 11
- #define TX_FLUSH 12
- #define RX_TRSHLD_MASK 0xf
- #define RX_TRSHLD_SHIFT 0
- #define TX_TRSHLD_MASK 0xf
- #define TX_TRSHLD_SHIFT 4
- #define TXFIFO 0x50
- #define RXFIFO 0x90
- #define MID 0xfc
- #define FIFO_DEPTH 16
- #define TX_TRSHLD 4
- #define RX_TRSHLD (FIFO_DEPTH - TX_TRSHLD)
- #define TXBIT 1
- #define RXBIT 2
- DECLARE_GLOBAL_DATA_PTR;
- struct synquacer_spi_plat {
- void __iomem *base;
- bool aces, rtm;
- };
- struct synquacer_spi_priv {
- void __iomem *base;
- bool aces, rtm;
- int speed, cs, mode, rwflag;
- void *rx_buf;
- const void *tx_buf;
- unsigned int tx_words, rx_words;
- };
- static void read_fifo(struct synquacer_spi_priv *priv)
- {
- u32 len = readl(priv->base + DMSTATUS);
- u8 *buf = priv->rx_buf;
- int i;
- len = (len >> RX_DATA_SHIFT) & RX_DATA_MASK;
- len = min_t(unsigned int, len, priv->rx_words);
- for (i = 0; i < len; i++)
- *buf++ = readb(priv->base + RXFIFO);
- priv->rx_buf = buf;
- priv->rx_words -= len;
- }
- static void write_fifo(struct synquacer_spi_priv *priv)
- {
- u32 len = readl(priv->base + DMSTATUS);
- const u8 *buf = priv->tx_buf;
- int i;
- len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK;
- len = min_t(unsigned int, FIFO_DEPTH - len, priv->tx_words);
- for (i = 0; i < len; i++)
- writeb(*buf++, priv->base + TXFIFO);
- priv->tx_buf = buf;
- priv->tx_words -= len;
- }
- static void synquacer_cs_set(struct synquacer_spi_priv *priv, bool active)
- {
- u32 val;
- val = readl(priv->base + DMSTART);
- val &= ~(CS_MASK << CS_SHIFT);
- val |= priv->cs << CS_SHIFT;
- if (active) {
- writel(val, priv->base + DMSTART);
- val = readl(priv->base + DMSTART);
- val &= ~BIT(DMSTOP);
- writel(val, priv->base + DMSTART);
- } else {
- val |= BIT(DMSTOP);
- writel(val, priv->base + DMSTART);
- if (priv->rx_buf) {
- u32 buf[16];
- priv->rx_buf = buf;
- priv->rx_words = 16;
- read_fifo(priv);
- }
- }
- }
- static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
- {
- struct udevice *bus = dev->parent;
- struct synquacer_spi_priv *priv = dev_get_priv(bus);
- struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 val, div, bus_width;
- int rwflag;
- rwflag = (rx ? 1 : 0) | (tx ? 2 : 0);
- /* if nothing to do */
- if (slave_plat->mode == priv->mode &&
- rwflag == priv->rwflag &&
- slave_plat->cs == priv->cs &&
- slave_plat->max_hz == priv->speed)
- return;
- priv->rwflag = rwflag;
- priv->cs = slave_plat->cs;
- priv->mode = slave_plat->mode;
- priv->speed = slave_plat->max_hz;
- if (priv->mode & SPI_TX_BYTE)
- bus_width = 1;
- else if (priv->mode & SPI_TX_DUAL)
- bus_width = 2;
- else if (priv->mode & SPI_TX_QUAD)
- bus_width = 4;
- else if (priv->mode & SPI_TX_OCTAL)
- bus_width = 8;
- div = DIV_ROUND_UP(125000000, priv->speed);
- val = readl(priv->base + PCC(priv->cs));
- val &= ~BIT(RTM);
- val &= ~BIT(ACES);
- val &= ~BIT(SAFESYNC);
- if ((priv->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3)
- val |= BIT(SAFESYNC);
- if ((priv->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6)
- val |= BIT(SAFESYNC);
- if (priv->mode & SPI_CPHA)
- val |= BIT(CPHA);
- else
- val &= ~BIT(CPHA);
- if (priv->mode & SPI_CPOL)
- val |= BIT(CPOL);
- else
- val &= ~BIT(CPOL);
- if (priv->mode & SPI_CS_HIGH)
- val |= BIT(SSPOL);
- else
- val &= ~BIT(SSPOL);
- if (priv->mode & SPI_LSB_FIRST)
- val |= BIT(SDIR);
- else
- val &= ~BIT(SDIR);
- if (priv->aces)
- val |= BIT(ACES);
- if (priv->rtm)
- val |= BIT(RTM);
- val |= (3 << SS2CD);
- val |= BIT(SENDIAN);
- val &= ~(CDRS_MASK << CDRS_SHIFT);
- val |= ((div >> 1) << CDRS_SHIFT);
- writel(val, priv->base + PCC(priv->cs));
- val = readl(priv->base + FIFOCFG);
- val &= ~(BPW_MASK << BPW_SHIFT);
- val |= (0 << BPW_SHIFT);
- writel(val, priv->base + FIFOCFG);
- val = readl(priv->base + DMSTART);
- val &= ~(DATA_MASK << DATA_SHIFT);
- if (tx && rx)
- val |= (DATA_TXRX << DATA_SHIFT);
- else if (rx)
- val |= (DATA_RX << DATA_SHIFT);
- else
- val |= (DATA_TX << DATA_SHIFT);
- val &= ~(3 << BUS_WIDTH);
- val |= ((bus_width >> 1) << BUS_WIDTH);
- writel(val, priv->base + DMSTART);
- }
- static int synquacer_spi_xfer(struct udevice *dev, unsigned int bitlen,
- const void *tx_buf, void *rx_buf,
- unsigned long flags)
- {
- struct udevice *bus = dev->parent;
- struct synquacer_spi_priv *priv = dev_get_priv(bus);
- u32 val, words, busy;
- val = readl(priv->base + FIFOCFG);
- val |= (1 << RX_FLUSH);
- val |= (1 << TX_FLUSH);
- writel(val, priv->base + FIFOCFG);
- synquacer_spi_config(dev, rx_buf, tx_buf);
- priv->tx_buf = tx_buf;
- priv->rx_buf = rx_buf;
- words = bitlen / 8;
- if (tx_buf) {
- busy |= BIT(TXBIT);
- priv->tx_words = words;
- } else {
- busy &= ~BIT(TXBIT);
- priv->tx_words = 0;
- }
- if (rx_buf) {
- busy |= BIT(RXBIT);
- priv->rx_words = words;
- } else {
- busy &= ~BIT(RXBIT);
- priv->rx_words = 0;
- }
- if (flags & SPI_XFER_BEGIN)
- synquacer_cs_set(priv, true);
- if (tx_buf)
- write_fifo(priv);
- if (rx_buf) {
- val = readl(priv->base + FIFOCFG);
- val &= ~(RX_TRSHLD_MASK << RX_TRSHLD_SHIFT);
- val |= ((priv->rx_words > FIFO_DEPTH ?
- RX_TRSHLD : priv->rx_words) << RX_TRSHLD_SHIFT);
- writel(val, priv->base + FIFOCFG);
- }
- writel(~0, priv->base + TXC);
- writel(~0, priv->base + RXC);
- /* Trigger */
- val = readl(priv->base + DMSTART);
- val |= BIT(TRIGGER);
- writel(val, priv->base + DMSTART);
- while (busy & (BIT(RXBIT) | BIT(TXBIT))) {
- if (priv->rx_words)
- read_fifo(priv);
- else
- busy &= ~BIT(RXBIT);
- if (priv->tx_words) {
- write_fifo(priv);
- } else {
- u32 len;
- do { /* wait for shifter to empty out */
- cpu_relax();
- len = readl(priv->base + DMSTATUS);
- len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK;
- } while (tx_buf && len);
- busy &= ~BIT(TXBIT);
- }
- }
- if (flags & SPI_XFER_END)
- synquacer_cs_set(priv, false);
- return 0;
- }
- static int synquacer_spi_set_speed(struct udevice *bus, uint speed)
- {
- return 0;
- }
- static int synquacer_spi_set_mode(struct udevice *bus, uint mode)
- {
- return 0;
- }
- static int synquacer_spi_claim_bus(struct udevice *dev)
- {
- return 0;
- }
- static int synquacer_spi_release_bus(struct udevice *dev)
- {
- return 0;
- }
- static void synquacer_spi_disable_module(struct synquacer_spi_priv *priv)
- {
- writel(0, priv->base + MCTRL);
- while (readl(priv->base + MCTRL) & BIT(MES))
- cpu_relax();
- }
- static void synquacer_spi_init(struct synquacer_spi_priv *priv)
- {
- u32 val;
- synquacer_spi_disable_module(priv);
- writel(0, priv->base + TXE);
- writel(0, priv->base + RXE);
- val = readl(priv->base + TXF);
- writel(val, priv->base + TXC);
- val = readl(priv->base + RXF);
- writel(val, priv->base + RXC);
- val = readl(priv->base + FAULTF);
- writel(val, priv->base + FAULTC);
- val = readl(priv->base + DMCFG);
- val &= ~BIT(SSDC);
- val &= ~BIT(MSTARTEN);
- writel(val, priv->base + DMCFG);
- /* Enable module with direct mode */
- val = readl(priv->base + MCTRL);
- val &= ~BIT(IPCLK);
- val &= ~BIT(CSEN);
- val |= BIT(MEN);
- val |= BIT(SYNCON);
- writel(val, priv->base + MCTRL);
- }
- static void synquacer_spi_exit(struct synquacer_spi_priv *priv)
- {
- u32 val;
- synquacer_spi_disable_module(priv);
- /* Enable module with command sequence mode */
- val = readl(priv->base + MCTRL);
- val &= ~BIT(IPCLK);
- val |= BIT(CSEN);
- val |= BIT(MEN);
- val |= BIT(SYNCON);
- writel(val, priv->base + MCTRL);
- while (!(readl(priv->base + MCTRL) & BIT(MES)))
- cpu_relax();
- }
- static int synquacer_spi_probe(struct udevice *bus)
- {
- struct synquacer_spi_plat *plat = dev_get_plat(bus);
- struct synquacer_spi_priv *priv = dev_get_priv(bus);
- priv->base = plat->base;
- priv->aces = plat->aces;
- priv->rtm = plat->rtm;
- synquacer_spi_init(priv);
- return 0;
- }
- static int synquacer_spi_remove(struct udevice *bus)
- {
- struct synquacer_spi_priv *priv = dev_get_priv(bus);
- synquacer_spi_exit(priv);
- return 0;
- }
- static int synquacer_spi_of_to_plat(struct udevice *bus)
- {
- struct synquacer_spi_plat *plat = dev_get_plat(bus);
- struct clk clk;
- plat->base = dev_read_addr_ptr(bus);
- plat->aces = dev_read_bool(bus, "socionext,set-aces");
- plat->rtm = dev_read_bool(bus, "socionext,use-rtm");
- clk_get_by_name(bus, "iHCLK", &clk);
- clk_enable(&clk);
- return 0;
- }
- static const struct dm_spi_ops synquacer_spi_ops = {
- .claim_bus = synquacer_spi_claim_bus,
- .release_bus = synquacer_spi_release_bus,
- .xfer = synquacer_spi_xfer,
- .set_speed = synquacer_spi_set_speed,
- .set_mode = synquacer_spi_set_mode,
- };
- static const struct udevice_id synquacer_spi_ids[] = {
- { .compatible = "socionext,synquacer-spi" },
- { /* Sentinel */ }
- };
- U_BOOT_DRIVER(synquacer_spi) = {
- .name = "synquacer_spi",
- .id = UCLASS_SPI,
- .of_match = synquacer_spi_ids,
- .ops = &synquacer_spi_ops,
- .of_to_plat = synquacer_spi_of_to_plat,
- .plat_auto = sizeof(struct synquacer_spi_plat),
- .priv_auto = sizeof(struct synquacer_spi_priv),
- .probe = synquacer_spi_probe,
- .flags = DM_FLAG_OS_PREPARE,
- .remove = synquacer_spi_remove,
- };
|