spi: sun4i: Access registers and bits via enum offsets
Allwinner support two different SPI controllers one for A10 and
another for A31 with minimal changes in register offsets and
respective register bits, but the logic for accessing the SPI
master via SPI slave remains nearly similar.
Add enum offsets for register set and register bits, so-that
it can access both classes of SPI controllers.
Assign same control register for global, transfer and fifo control
registers to make the same code compatible with A31 SPI controller.
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
Tested-by: Stefan Mavrodiev <stefan@olimex.com> # A20-SOM204
diff --git a/drivers/spi/sun4i_spi.c b/drivers/spi/sun4i_spi.c
index 0b16630..be026e6 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
{
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 @@
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 @@
*/
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 @@
}
priv->freq = speed;
- writel(reg, &priv->regs->cctl);
+ writel(reg, SPI_REG(priv, SPI_CCR));
return 0;
}
@@ -415,17 +450,17 @@
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 @@
.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,
+ },
{ }
};