Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olemmela/83bf430448bbb86608618944d60f673c to your computer and use it in GitHub Desktop.
Save olemmela/83bf430448bbb86608618944d60f673c to your computer and use it in GitHub Desktop.
From ffdd7cc3da6c913f86548fb502978271b3ddca96 Mon Sep 17 00:00:00 2001
From: Oskari Lemmela <oskari@lemmela.net>
Date: Sat, 29 Dec 2018 10:55:20 +0200
Subject: [PATCH] arm: sunxi: Allwinner SPI driver sun6i support
Minimal changes to support sun6i based Allwinner SOCs
Changes are based to SPL driver arch/arm/mach-sunxi/spl_spi_sunxi.c
Signed-off-by: Oskari Lemmela <oskari@lemmela.net>
---
arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 1 +
arch/arm/include/asm/arch-sunxi/clock_sun9i.h | 1 +
drivers/spi/Kconfig | 4 +-
drivers/spi/sun4i_spi.c | 115 ++++++++++++++++--
4 files changed, 106 insertions(+), 15 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
index ee387127f3..4aaa0932d7 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
@@ -321,6 +321,7 @@ struct sunxi_ccm_reg {
#define AHB_GATE_OFFSET_MMC(n) (AHB_GATE_OFFSET_MMC0 + (n))
#define AHB_GATE_OFFSET_DMA 6
#define AHB_GATE_OFFSET_SS 5
+#define AHB_GATE_OFFSET_SPI0 20
/* ahb_gate1 offsets */
#define AHB_GATE_OFFSET_DRC0 25
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
index 530e0dd73b..9bbd4d319e 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
@@ -194,6 +194,7 @@ struct sunxi_ccm_reg {
/* ahb gate1 field */
#define AHB_GATE_OFFSET_DMA 24
+#define AHB_GATE_OFFSET_SPI0 20
/* apb1_gate fields */
#define APB1_GATE_UART_SHIFT 16
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a7bb5b35c2..88e772cb1a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -219,9 +219,9 @@ config STM32_QSPI
this ST IP core.
config SUN4I_SPI
- bool "Allwinner A10 SoCs SPI controller"
+ bool "Allwinner SoCs SPI driver"
help
- SPI driver for Allwinner sun4i, sun5i and sun7i SoCs
+ SPI driver for Allwinner SoCs
config TEGRA114_SPI
bool "nVidia Tegra114 SPI driver"
diff --git a/drivers/spi/sun4i_spi.c b/drivers/spi/sun4i_spi.c
index b86b5a00ad..75ae1a041e 100644
--- a/drivers/spi/sun4i_spi.c
+++ b/drivers/spi/sun4i_spi.c
@@ -37,6 +37,30 @@
#define SUN4I_TXDATA_REG 0x04
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+#define SUN4I_CTL_REG 0x04
+#define SUN4I_CTL_ENABLE BIT(0)
+#define SUN4I_CTL_MASTER BIT(1)
+#define SUN4I_CTL_TP BIT(7)
+#define SUN4I_CTL_SRST BIT(31)
+
+#define SUN4I_CTL_CPHA BIT(0)
+#define SUN4I_CTL_CPOL BIT(1)
+#define SUN4I_CTL_CS_ACTIVE_LOW BIT(2)
+#define SUN4I_CTL_CS_MASK 0x30
+#define SUN4I_CTL_CS(cs) (((cs) << 4) & SUN4I_CTL_CS_MASK)
+#define SUN4I_CTL_CS_MANUAL BIT(6)
+#define SUN4I_CTL_CS_LEVEL BIT(7)
+#define SUN4I_CTL_DHB BIT(8)
+#define SUN4I_CTL_XCH_MASK 0x80000000
+#define SUN4I_CTL_XCH BIT(31)
+
+#define SUN4I_CTL_RF_RST BIT(15)
+#define SUN4I_CTL_TF_RST BIT(31)
+
+#else
+#define SUN4I_CTL_SRST 0
+
#define SUN4I_CTL_REG 0x08
#define SUN4I_CTL_ENABLE BIT(0)
#define SUN4I_CTL_MASTER BIT(1)
@@ -54,6 +78,7 @@
#define SUN4I_CTL_CS_MANUAL BIT(16)
#define SUN4I_CTL_CS_LEVEL BIT(17)
#define SUN4I_CTL_TP BIT(18)
+#endif
#define SUN4I_INT_CTL_REG 0x0c
#define SUN4I_INT_CTL_RF_F34 BIT(4)
@@ -92,11 +117,41 @@
#define SUN4I_SPI_DEFAULT_RATE 1000000
#define SUN4I_SPI_TIMEOUT_US 1000000
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+
+/* sun6i spi register set */
+struct sun4i_spi_regs {
+ u32 res0;
+ u32 ctl; /* 0x04 */
+ u32 tctl; /* 0x08 */
+ u32 res1;
+ u32 intctl; /* 0x10 */
+ u32 st; /* 0x14 */
+ u32 fifo_ctl; /* 0x18 */
+ u32 fifo_sta; /* 0x1c */
+ u32 wait; /* 0x20 */
+ u32 cctl; /* 0x24 */
+ u32 res2[2];
+ u32 bc; /* 0x30 */
+ u32 tc; /* 0x34 */
+ u32 bctl; /* 0x38 */
+ u32 res3[113];
+ u32 txdata; /* 0x200 */
+ u32 res4[63];
+ u32 rxdata; /* 0x300 */
+};
+
+#else
/* sun4i spi register set */
struct sun4i_spi_regs {
u32 rxdata;
u32 txdata;
- u32 ctl;
+ union {
+ u32 ctl;
+ u32 tctl;
+ u32 fifo_ctl;
+ u32 bctl;
+ };
u32 intctl;
u32 st;
u32 dmactl;
@@ -106,6 +161,7 @@ struct sun4i_spi_regs {
u32 tc;
u32 fifo_sta;
};
+#endif
struct sun4i_spi_platdata {
u32 base_addr;
@@ -148,7 +204,7 @@ 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(&priv->regs->tctl);
reg &= ~SUN4I_CTL_CS_MASK;
reg |= SUN4I_CTL_CS(cs);
@@ -158,7 +214,7 @@ static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable)
else
reg |= SUN4I_CTL_CS_LEVEL;
- writel(reg, &priv->regs->ctl);
+ writel(reg, &priv->regs->tctl);
}
static int sun4i_spi_parse_pins(struct udevice *dev)
@@ -230,7 +286,10 @@ static int sun4i_spi_parse_pins(struct udevice *dev)
if (pin < 0)
break;
- sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
+ if (IS_ENABLED(CONFIG_MACH_SUN50I))
+ sunxi_gpio_set_cfgpin(pin, SUN50I_GPC_SPI0);
+ else
+ sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
sunxi_gpio_set_drv(pin, drive);
sunxi_gpio_set_pull(pin, pull);
}
@@ -243,10 +302,27 @@ static inline void sun4i_spi_enable_clock(void)
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *const)SUNXI_CCM_BASE;
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+ setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_SPI0));
+#endif
+
setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_SPI0));
writel((1 << 31), &ccm->spi0_clk_cfg);
}
+static inline void sun4i_spi_disable_clock(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *const)SUNXI_CCM_BASE;
+
+ writel(0, &ccm->spi0_clk_cfg);
+ clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_SPI0));
+
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+ clrbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_SPI0));
+#endif
+}
+
static int sun4i_spi_ofdata_to_platdata(struct udevice *bus)
{
struct sun4i_spi_platdata *plat = dev_get_platdata(bus);
@@ -268,7 +344,6 @@ static int sun4i_spi_probe(struct udevice *bus)
struct sun4i_spi_platdata *plat = dev_get_platdata(bus);
struct sun4i_spi_priv *priv = dev_get_priv(bus);
- sun4i_spi_enable_clock();
sun4i_spi_parse_pins(bus);
priv->regs = (struct sun4i_spi_regs *)(uintptr_t)plat->base_addr;
@@ -281,9 +356,17 @@ static int sun4i_spi_claim_bus(struct udevice *dev)
{
struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
+ sun4i_spi_enable_clock();
writel(SUN4I_CTL_ENABLE | SUN4I_CTL_MASTER | SUN4I_CTL_TP |
- SUN4I_CTL_CS_MANUAL | SUN4I_CTL_CS_ACTIVE_LOW,
+ SUN4I_CTL_SRST,
&priv->regs->ctl);
+
+ if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
+ while (readl(&priv->regs->ctl) & SUN4I_CTL_SRST)
+ ;
+
+ setbits_le32(&priv->regs->tctl, SUN4I_CTL_CS_MANUAL |
+ SUN4I_CTL_CS_ACTIVE_LOW);
return 0;
}
@@ -295,6 +378,7 @@ static int sun4i_spi_release_bus(struct udevice *dev)
reg = readl(&priv->regs->ctl);
reg &= ~SUN4I_CTL_ENABLE;
writel(reg, &priv->regs->ctl);
+ sun4i_spi_disable_clock();
return 0;
}
@@ -322,10 +406,10 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (flags & SPI_XFER_BEGIN)
sun4i_spi_set_cs(bus, slave_plat->cs, true);
- reg = readl(&priv->regs->ctl);
+ reg = readl(&priv->regs->fifo_ctl);
/* Reset FIFOs */
- writel(reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST, &priv->regs->ctl);
+ writel(reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST, &priv->regs->fifo_ctl);
while (len) {
/* Setup the transfer now... */
@@ -334,16 +418,18 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Setup the counters */
writel(SUN4I_BURST_CNT(nbytes), &priv->regs->bc);
writel(SUN4I_XMIT_CNT(nbytes), &priv->regs->tc);
+ if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
+ writel(SUN4I_BURST_CNT(nbytes), &priv->regs->bctl);
/* Fill the TX FIFO */
sun4i_spi_fill_fifo(priv, nbytes);
/* Start the transfer */
- reg = readl(&priv->regs->ctl);
- writel(reg | SUN4I_CTL_XCH, &priv->regs->ctl);
+ reg = readl(&priv->regs->tctl);
+ writel(reg | SUN4I_CTL_XCH, &priv->regs->tctl);
/* Wait transfer to complete */
- ret = wait_for_bit_le32(&priv->regs->ctl, SUN4I_CTL_XCH_MASK,
+ ret = wait_for_bit_le32(&priv->regs->tctl, SUN4I_CTL_XCH_MASK,
false, SUN4I_SPI_TIMEOUT_US, false);
if (ret) {
printf("ERROR: sun4i_spi: Timeout transferring data\n");
@@ -416,7 +502,7 @@ 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 = readl(&priv->regs->tctl);
reg &= ~(SUN4I_CTL_CPOL | SUN4I_CTL_CPHA);
if (mode & SPI_CPOL)
@@ -426,7 +512,7 @@ static int sun4i_spi_set_mode(struct udevice *dev, uint mode)
reg |= SUN4I_CTL_CPHA;
priv->mode = mode;
- writel(reg, &priv->regs->ctl);
+ writel(reg, &priv->regs->tctl);
return 0;
}
@@ -441,6 +527,9 @@ static const struct dm_spi_ops sun4i_spi_ops = {
static const struct udevice_id sun4i_spi_ids[] = {
{ .compatible = "allwinner,sun4i-a10-spi" },
+ { .compatible = "allwinner,sun6i-a31-spi" },
+ { .compatible = "allwinner,sun8i-h3-spi" },
+ { .compatible = "allwinner,sun50i-a64-spi" },
{ }
};
--
2.17.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment