diff options
Diffstat (limited to 'drivers/spi/sun4i_spi.c')
-rw-r--r-- | drivers/spi/sun4i_spi.c | 154 |
1 files changed, 112 insertions, 42 deletions
diff --git a/drivers/spi/sun4i_spi.c b/drivers/spi/sun4i_spi.c index 0b1663038c..be026e6554 100644 --- a/drivers/spi/sun4i_spi.c +++ b/drivers/spi/sun4i_spi.c @@ -83,7 +83,6 @@ #define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) #define SUN4I_FIFO_STA_REG 0x28 -#define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f #define SUN4I_FIFO_STA_RF_CNT_BITS 0 #define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f #define SUN4I_FIFO_STA_TF_CNT_BITS 16 @@ -93,28 +92,56 @@ #define SUN4I_SPI_DEFAULT_RATE 1000000 #define SUN4I_SPI_TIMEOUT_US 1000000 -/* sun4i spi register set */ -struct sun4i_spi_regs { - u32 rxdata; - u32 txdata; - u32 ctl; - u32 intctl; - u32 st; - u32 dmactl; - u32 wait; - u32 cctl; - u32 bc; - u32 tc; - u32 fifo_sta; +#define SPI_REG(priv, reg) ((priv)->base_addr + \ + (priv)->variant->regs[reg]) +#define SPI_BIT(priv, bit) ((priv)->variant->bits[bit]) +#define SPI_CS(priv, cs) (((cs) << SPI_BIT(priv, SPI_TCR_CS_SEL)) & \ + SPI_BIT(priv, SPI_TCR_CS_MASK)) + +/* sun spi register set */ +enum sun4i_spi_regs { + SPI_GCR, + SPI_TCR, + SPI_FCR, + SPI_FSR, + SPI_CCR, + SPI_BC, + SPI_TC, + SPI_BCTL, + SPI_TXD, + SPI_RXD, +}; + +/* sun spi register bits */ +enum sun4i_spi_bits { + SPI_GCR_TP, + SPI_TCR_CPHA, + SPI_TCR_CPOL, + SPI_TCR_CS_ACTIVE_LOW, + SPI_TCR_CS_SEL, + SPI_TCR_CS_MASK, + SPI_TCR_XCH, + SPI_TCR_CS_MANUAL, + SPI_TCR_CS_LEVEL, + SPI_FCR_TF_RST, + SPI_FCR_RF_RST, + SPI_FSR_RF_CNT_MASK, +}; + +struct sun4i_spi_variant { + const unsigned long *regs; + const u32 *bits; }; struct sun4i_spi_platdata { + struct sun4i_spi_variant *variant; u32 base_addr; u32 max_hz; }; struct sun4i_spi_priv { - struct sun4i_spi_regs *regs; + struct sun4i_spi_variant *variant; + u32 base_addr; u32 freq; u32 mode; @@ -129,7 +156,7 @@ static inline void sun4i_spi_drain_fifo(struct sun4i_spi_priv *priv, int len) u8 byte; while (len--) { - byte = readb(&priv->regs->rxdata); + byte = readb(SPI_REG(priv, SPI_RXD)); if (priv->rx_buf) *priv->rx_buf++ = byte; } @@ -141,7 +168,7 @@ static inline void sun4i_spi_fill_fifo(struct sun4i_spi_priv *priv, int len) while (len--) { byte = priv->tx_buf ? *priv->tx_buf++ : 0; - writeb(byte, &priv->regs->txdata); + writeb(byte, SPI_REG(priv, SPI_TXD)); } } @@ -150,17 +177,17 @@ static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable) struct sun4i_spi_priv *priv = dev_get_priv(bus); u32 reg; - reg = readl(&priv->regs->ctl); + reg = readl(SPI_REG(priv, SPI_TCR)); - reg &= ~SUN4I_CTL_CS_MASK; - reg |= SUN4I_CTL_CS(cs); + reg &= ~SPI_BIT(priv, SPI_TCR_CS_MASK); + reg |= SPI_CS(priv, cs); if (enable) - reg &= ~SUN4I_CTL_CS_LEVEL; + reg &= ~SPI_BIT(priv, SPI_TCR_CS_LEVEL); else - reg |= SUN4I_CTL_CS_LEVEL; + reg |= SPI_BIT(priv, SPI_TCR_CS_LEVEL); - writel(reg, &priv->regs->ctl); + writel(reg, SPI_REG(priv, SPI_TCR)); } static int sun4i_spi_parse_pins(struct udevice *dev) @@ -255,6 +282,7 @@ static int sun4i_spi_ofdata_to_platdata(struct udevice *bus) int node = dev_of_offset(bus); plat->base_addr = devfdt_get_addr(bus); + plat->variant = (struct sun4i_spi_variant *)dev_get_driver_data(bus); plat->max_hz = fdtdec_get_int(gd->fdt_blob, node, "spi-max-frequency", SUN4I_SPI_DEFAULT_RATE); @@ -273,7 +301,8 @@ static int sun4i_spi_probe(struct udevice *bus) sun4i_spi_enable_clock(); sun4i_spi_parse_pins(bus); - priv->regs = (struct sun4i_spi_regs *)(uintptr_t)plat->base_addr; + priv->variant = plat->variant; + priv->base_addr = plat->base_addr; priv->freq = plat->max_hz; return 0; @@ -283,9 +312,11 @@ static int sun4i_spi_claim_bus(struct udevice *dev) { struct sun4i_spi_priv *priv = dev_get_priv(dev->parent); - setbits_le32(&priv->regs->ctl, SUN4I_CTL_ENABLE | - SUN4I_CTL_MASTER | SUN4I_CTL_TP | - SUN4I_CTL_CS_MANUAL | SUN4I_CTL_CS_ACTIVE_LOW); + setbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE | + SUN4I_CTL_MASTER | SPI_BIT(priv, SPI_GCR_TP)); + + setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) | + SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW)); return 0; } @@ -294,7 +325,7 @@ static int sun4i_spi_release_bus(struct udevice *dev) { struct sun4i_spi_priv *priv = dev_get_priv(dev->parent); - clrbits_le32(&priv->regs->ctl, SUN4I_CTL_ENABLE); + clrbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE); return 0; } @@ -323,25 +354,29 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen, sun4i_spi_set_cs(bus, slave_plat->cs, true); /* Reset FIFOs */ - setbits_le32(&priv->regs->ctl, SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); + setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) | + SPI_BIT(priv, SPI_FCR_TF_RST)); while (len) { /* Setup the transfer now... */ nbytes = min(len, (u32)(SUN4I_FIFO_DEPTH - 1)); /* Setup the counters */ - writel(SUN4I_BURST_CNT(nbytes), &priv->regs->bc); - writel(SUN4I_XMIT_CNT(nbytes), &priv->regs->tc); + writel(SUN4I_BURST_CNT(nbytes), SPI_REG(priv, SPI_BC)); + writel(SUN4I_XMIT_CNT(nbytes), SPI_REG(priv, SPI_TC)); /* Fill the TX FIFO */ sun4i_spi_fill_fifo(priv, nbytes); /* Start the transfer */ - setbits_le32(&priv->regs->ctl, SUN4I_CTL_XCH); + setbits_le32(SPI_REG(priv, SPI_TCR), + SPI_BIT(priv, SPI_TCR_XCH)); /* Wait till RX FIFO to be empty */ - ret = readl_poll_timeout(&priv->regs->fifo_sta, rx_fifocnt, - (((rx_fifocnt & SUN4I_FIFO_STA_RF_CNT_MASK) >> + ret = readl_poll_timeout(SPI_REG(priv, SPI_FSR), + rx_fifocnt, + (((rx_fifocnt & + SPI_BIT(priv, SPI_FSR_RF_CNT_MASK)) >> SUN4I_FIFO_STA_RF_CNT_BITS) >= nbytes), SUN4I_SPI_TIMEOUT_US); if (ret < 0) { @@ -390,7 +425,7 @@ static int sun4i_spi_set_speed(struct udevice *dev, uint speed) */ div = SUN4I_SPI_MAX_RATE / (2 * speed); - reg = readl(&priv->regs->cctl); + reg = readl(SPI_REG(priv, SPI_CCR)); if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { if (div > 0) @@ -405,7 +440,7 @@ static int sun4i_spi_set_speed(struct udevice *dev, uint speed) } priv->freq = speed; - writel(reg, &priv->regs->cctl); + writel(reg, SPI_REG(priv, SPI_CCR)); return 0; } @@ -415,17 +450,17 @@ static int sun4i_spi_set_mode(struct udevice *dev, uint mode) struct sun4i_spi_priv *priv = dev_get_priv(dev); u32 reg; - reg = readl(&priv->regs->ctl); - reg &= ~(SUN4I_CTL_CPOL | SUN4I_CTL_CPHA); + reg = readl(SPI_REG(priv, SPI_TCR)); + reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA)); if (mode & SPI_CPOL) - reg |= SUN4I_CTL_CPOL; + reg |= SPI_BIT(priv, SPI_TCR_CPOL); if (mode & SPI_CPHA) - reg |= SUN4I_CTL_CPHA; + reg |= SPI_BIT(priv, SPI_TCR_CPHA); priv->mode = mode; - writel(reg, &priv->regs->ctl); + writel(reg, SPI_REG(priv, SPI_TCR)); return 0; } @@ -438,8 +473,43 @@ static const struct dm_spi_ops sun4i_spi_ops = { .set_mode = sun4i_spi_set_mode, }; +static const unsigned long sun4i_spi_regs[] = { + [SPI_GCR] = SUN4I_CTL_REG, + [SPI_TCR] = SUN4I_CTL_REG, + [SPI_FCR] = SUN4I_CTL_REG, + [SPI_FSR] = SUN4I_FIFO_STA_REG, + [SPI_CCR] = SUN4I_CLK_CTL_REG, + [SPI_BC] = SUN4I_BURST_CNT_REG, + [SPI_TC] = SUN4I_XMIT_CNT_REG, + [SPI_TXD] = SUN4I_TXDATA_REG, + [SPI_RXD] = SUN4I_RXDATA_REG, +}; + +static const u32 sun4i_spi_bits[] = { + [SPI_GCR_TP] = BIT(18), + [SPI_TCR_CPHA] = BIT(2), + [SPI_TCR_CPOL] = BIT(3), + [SPI_TCR_CS_ACTIVE_LOW] = BIT(4), + [SPI_TCR_XCH] = BIT(10), + [SPI_TCR_CS_SEL] = 12, + [SPI_TCR_CS_MASK] = 0x3000, + [SPI_TCR_CS_MANUAL] = BIT(16), + [SPI_TCR_CS_LEVEL] = BIT(17), + [SPI_FCR_TF_RST] = BIT(8), + [SPI_FCR_RF_RST] = BIT(9), + [SPI_FSR_RF_CNT_MASK] = GENMASK(6, 0), +}; + +static const struct sun4i_spi_variant sun4i_a10_spi_variant = { + .regs = sun4i_spi_regs, + .bits = sun4i_spi_bits, +}; + static const struct udevice_id sun4i_spi_ids[] = { - { .compatible = "allwinner,sun4i-a10-spi" }, + { + .compatible = "allwinner,sun4i-a10-spi", + .data = (ulong)&sun4i_a10_spi_variant, + }, { } }; |