[U-Boot] [PATCH v2 0/4] sunxi: SPI: Add SUN6I generation SPI support

We already have a driver for the SPI devices in older Allwinner CPUs. Newer SoCs (SUN6I generation) have a similar, but improved IP block. This series adds support for that, so we can access the SPI flash that some newer boards come with. This series gathers some patches that have appeared on the list in one form or another before (hence the v2 tag), but brings them all together and updates them.
The SUN4I/SUN6I SPI devices share a very similar architecure, but differ annoyingly in quite some details, to a point where a shared driver would look unnecessarily complicated. So we use an #ifdef based approach, which keeps the driver simple, at the cost of not being able to access both types of device in one binary build. But since there are no SoCs which feature both types, this is a theoretical disadvantage.
Patch 1/4 moves the clock toggling from the probe/remove to the claim/release-bus stage. Patch 2/4 adds the SPI gate clocks and reset gates to the clock drivers, also describes at least the enable bit for the SPI mod clock. This is used in patch 3/4, which uses the new DM clock framework in the driver, helping to abstract differences between the SUN4I/SUN6I types. Patch 4/4 eventually adds support for the new SUN6I generation SPI device. The actual device support is determined at compile time, based on the CONFIG_SUNXI_GEN_SUN6I symbol.
What's left out of this series is the config bits needed to actually activate the support for a board. At the moment this is unnecessarily verbose, so that Oskari is looking at simplifying this at the Kconfig level, which would complement this series.
This has been briefly tested on the Pine64-LTS and the OrangePi PC2 board. Please test them if you have other devices with a SUN6I SPI generation.
Cheers, Andre.
Jagan Teki (2): clk: sunxi: Implement SPI gates clocks and reset spi: sunxi: Add CLK support to Allwinner SPI driver
Oskari Lemmela (2): spi: sunxi: Enable and disable SPI clock on bus claim/release spi: sunxi: Support newer SoCs
drivers/clk/sunxi/clk_a10.c | 9 +++ drivers/clk/sunxi/clk_a10s.c | 7 ++ drivers/clk/sunxi/clk_a23.c | 7 ++ drivers/clk/sunxi/clk_a31.c | 13 ++++ drivers/clk/sunxi/clk_a64.c | 7 ++ drivers/clk/sunxi/clk_a80.c | 15 ++++ drivers/clk/sunxi/clk_a83t.c | 7 ++ drivers/clk/sunxi/clk_h3.c | 7 ++ drivers/clk/sunxi/clk_h6.c | 6 ++ drivers/clk/sunxi/clk_r40.c | 13 ++++ drivers/clk/sunxi/clk_v3s.c | 4 + drivers/spi/Kconfig | 4 +- drivers/spi/sun4i_spi.c | 177 +++++++++++++++++++++++++++++++++++++------ 13 files changed, 252 insertions(+), 24 deletions(-)

From: Oskari Lemmela oskari@lemmela.net
Introduce a function to disable the SPI clock, and turn it on and off on bus claim/release, instead of enabling it just once at probe time.
Signed-off-by: Oskari Lemmela oskari@lemmela.net Signed-off-by: Andre Przywara andre.przywara@arm.com --- drivers/spi/sun4i_spi.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/sun4i_spi.c b/drivers/spi/sun4i_spi.c index 38cc743c61..61a5f3522e 100644 --- a/drivers/spi/sun4i_spi.c +++ b/drivers/spi/sun4i_spi.c @@ -239,7 +239,7 @@ static int sun4i_spi_parse_pins(struct udevice *dev) return 0; }
-static inline void sun4i_spi_enable_clock(void) +static void sun4i_spi_enable_clock(void) { struct sunxi_ccm_reg *const ccm = (struct sunxi_ccm_reg *const)SUNXI_CCM_BASE; @@ -248,6 +248,16 @@ static inline void sun4i_spi_enable_clock(void) writel((1 << 31), &ccm->spi0_clk_cfg); }
+static 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)); + +} + static int sun4i_spi_ofdata_to_platdata(struct udevice *bus) { struct sun4i_spi_platdata *plat = dev_get_platdata(bus); @@ -269,7 +279,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; @@ -282,9 +291,12 @@ 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, &priv->regs->ctl); + return 0; }
@@ -297,6 +309,8 @@ static int sun4i_spi_release_bus(struct udevice *dev) reg &= ~SUN4I_CTL_ENABLE; writel(reg, &priv->regs->ctl);
+ sun4i_spi_disable_clock(); + return 0; }

From: Jagan Teki jagan@amarulasolutions.com
In every SoC the SPI device requires an AHB clock, also the MOD clock needs to be toggled. Provide those as gate clocks for all SoCs as per the manual. The driver doesn't tinker with the actual SPI frequency, so we get away without a proper MOD clock implementation.
Newer SoCs also provide a reset gate per SPI device. Add them as well.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com Signed-off-by: Andre Przywara andre.przywara@arm.com [Andre: Add A23, A31, A80, A83T, H3, H6, R40, V3s] --- drivers/clk/sunxi/clk_a10.c | 9 +++++++++ drivers/clk/sunxi/clk_a10s.c | 7 +++++++ drivers/clk/sunxi/clk_a23.c | 7 +++++++ drivers/clk/sunxi/clk_a31.c | 13 +++++++++++++ drivers/clk/sunxi/clk_a64.c | 7 +++++++ drivers/clk/sunxi/clk_a80.c | 15 +++++++++++++++ drivers/clk/sunxi/clk_a83t.c | 7 +++++++ drivers/clk/sunxi/clk_h3.c | 7 +++++++ drivers/clk/sunxi/clk_h6.c | 6 ++++++ drivers/clk/sunxi/clk_r40.c | 13 +++++++++++++ drivers/clk/sunxi/clk_v3s.c | 4 ++++ 11 files changed, 95 insertions(+)
diff --git a/drivers/clk/sunxi/clk_a10.c b/drivers/clk/sunxi/clk_a10.c index 2aa41efe17..654b954203 100644 --- a/drivers/clk/sunxi/clk_a10.c +++ b/drivers/clk/sunxi/clk_a10.c @@ -22,6 +22,10 @@ static struct ccu_clk_gate a10_gates[] = { [CLK_AHB_MMC1] = GATE(0x060, BIT(9)), [CLK_AHB_MMC2] = GATE(0x060, BIT(10)), [CLK_AHB_MMC3] = GATE(0x060, BIT(11)), + [CLK_AHB_SPI0] = GATE(0x060, BIT(20)), + [CLK_AHB_SPI1] = GATE(0x060, BIT(21)), + [CLK_AHB_SPI2] = GATE(0x060, BIT(22)), + [CLK_AHB_SPI3] = GATE(0x060, BIT(23)),
[CLK_APB1_UART0] = GATE(0x06c, BIT(16)), [CLK_APB1_UART1] = GATE(0x06c, BIT(17)), @@ -32,6 +36,11 @@ static struct ccu_clk_gate a10_gates[] = { [CLK_APB1_UART6] = GATE(0x06c, BIT(22)), [CLK_APB1_UART7] = GATE(0x06c, BIT(23)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_SPI2] = GATE(0x0a8, BIT(31)), + [CLK_SPI3] = GATE(0x0d4, BIT(31)), /* SPI3 is separate */ + [CLK_USB_OHCI0] = GATE(0x0cc, BIT(6)), [CLK_USB_OHCI1] = GATE(0x0cc, BIT(7)), [CLK_USB_PHY] = GATE(0x0cc, BIT(8)), diff --git a/drivers/clk/sunxi/clk_a10s.c b/drivers/clk/sunxi/clk_a10s.c index 87b74e52dc..0a417d0b9f 100644 --- a/drivers/clk/sunxi/clk_a10s.c +++ b/drivers/clk/sunxi/clk_a10s.c @@ -19,6 +19,13 @@ static struct ccu_clk_gate a10s_gates[] = { [CLK_AHB_MMC0] = GATE(0x060, BIT(8)), [CLK_AHB_MMC1] = GATE(0x060, BIT(9)), [CLK_AHB_MMC2] = GATE(0x060, BIT(10)), + [CLK_AHB_SPI0] = GATE(0x060, BIT(20)), + [CLK_AHB_SPI1] = GATE(0x060, BIT(21)), + [CLK_AHB_SPI2] = GATE(0x060, BIT(22)), + + [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_SPI2] = GATE(0x0a8, BIT(31)),
[CLK_APB1_UART0] = GATE(0x06c, BIT(16)), [CLK_APB1_UART1] = GATE(0x06c, BIT(17)), diff --git a/drivers/clk/sunxi/clk_a23.c b/drivers/clk/sunxi/clk_a23.c index 1ef2359286..c16019215e 100644 --- a/drivers/clk/sunxi/clk_a23.c +++ b/drivers/clk/sunxi/clk_a23.c @@ -16,6 +16,8 @@ static struct ccu_clk_gate a23_gates[] = { [CLK_BUS_MMC0] = GATE(0x060, BIT(8)), [CLK_BUS_MMC1] = GATE(0x060, BIT(9)), [CLK_BUS_MMC2] = GATE(0x060, BIT(10)), + [CLK_BUS_SPI0] = GATE(0x060, BIT(20)), + [CLK_BUS_SPI1] = GATE(0x060, BIT(21)), [CLK_BUS_OTG] = GATE(0x060, BIT(24)), [CLK_BUS_EHCI] = GATE(0x060, BIT(26)), [CLK_BUS_OHCI] = GATE(0x060, BIT(29)), @@ -26,6 +28,9 @@ static struct ccu_clk_gate a23_gates[] = { [CLK_BUS_UART3] = GATE(0x06c, BIT(19)), [CLK_BUS_UART4] = GATE(0x06c, BIT(20)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), [CLK_USB_PHY1] = GATE(0x0cc, BIT(9)), [CLK_USB_HSIC] = GATE(0x0cc, BIT(10)), @@ -41,6 +46,8 @@ static struct ccu_reset a23_resets[] = { [RST_BUS_MMC0] = RESET(0x2c0, BIT(8)), [RST_BUS_MMC1] = RESET(0x2c0, BIT(9)), [RST_BUS_MMC2] = RESET(0x2c0, BIT(10)), + [RST_BUS_SPI0] = RESET(0x2c0, BIT(20)), + [RST_BUS_SPI1] = RESET(0x2c0, BIT(21)), [RST_BUS_OTG] = RESET(0x2c0, BIT(24)), [RST_BUS_EHCI] = RESET(0x2c0, BIT(26)), [RST_BUS_OHCI] = RESET(0x2c0, BIT(29)), diff --git a/drivers/clk/sunxi/clk_a31.c b/drivers/clk/sunxi/clk_a31.c index 5bd8b7dccc..fa6e3eeef0 100644 --- a/drivers/clk/sunxi/clk_a31.c +++ b/drivers/clk/sunxi/clk_a31.c @@ -17,6 +17,10 @@ static struct ccu_clk_gate a31_gates[] = { [CLK_AHB1_MMC1] = GATE(0x060, BIT(9)), [CLK_AHB1_MMC2] = GATE(0x060, BIT(10)), [CLK_AHB1_MMC3] = GATE(0x060, BIT(11)), + [CLK_AHB1_SPI0] = GATE(0x060, BIT(20)), + [CLK_AHB1_SPI1] = GATE(0x060, BIT(21)), + [CLK_AHB1_SPI2] = GATE(0x060, BIT(22)), + [CLK_AHB1_SPI3] = GATE(0x060, BIT(23)), [CLK_AHB1_OTG] = GATE(0x060, BIT(24)), [CLK_AHB1_EHCI0] = GATE(0x060, BIT(26)), [CLK_AHB1_EHCI1] = GATE(0x060, BIT(27)), @@ -31,6 +35,11 @@ static struct ccu_clk_gate a31_gates[] = { [CLK_APB2_UART4] = GATE(0x06c, BIT(20)), [CLK_APB2_UART5] = GATE(0x06c, BIT(21)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_SPI2] = GATE(0x0a8, BIT(31)), + [CLK_SPI3] = GATE(0x0ac, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), [CLK_USB_PHY1] = GATE(0x0cc, BIT(9)), [CLK_USB_PHY2] = GATE(0x0cc, BIT(10)), @@ -48,6 +57,10 @@ static struct ccu_reset a31_resets[] = { [RST_AHB1_MMC1] = RESET(0x2c0, BIT(9)), [RST_AHB1_MMC2] = RESET(0x2c0, BIT(10)), [RST_AHB1_MMC3] = RESET(0x2c0, BIT(11)), + [RST_AHB1_SPI0] = RESET(0x2c0, BIT(20)), + [RST_AHB1_SPI1] = RESET(0x2c0, BIT(21)), + [RST_AHB1_SPI2] = RESET(0x2c0, BIT(22)), + [RST_AHB1_SPI3] = RESET(0x2c0, BIT(23)), [RST_AHB1_OTG] = RESET(0x2c0, BIT(24)), [RST_AHB1_EHCI0] = RESET(0x2c0, BIT(26)), [RST_AHB1_EHCI1] = RESET(0x2c0, BIT(27)), diff --git a/drivers/clk/sunxi/clk_a64.c b/drivers/clk/sunxi/clk_a64.c index 910275fbce..322d6cd557 100644 --- a/drivers/clk/sunxi/clk_a64.c +++ b/drivers/clk/sunxi/clk_a64.c @@ -16,6 +16,8 @@ static const struct ccu_clk_gate a64_gates[] = { [CLK_BUS_MMC0] = GATE(0x060, BIT(8)), [CLK_BUS_MMC1] = GATE(0x060, BIT(9)), [CLK_BUS_MMC2] = GATE(0x060, BIT(10)), + [CLK_BUS_SPI0] = GATE(0x060, BIT(20)), + [CLK_BUS_SPI1] = GATE(0x060, BIT(21)), [CLK_BUS_OTG] = GATE(0x060, BIT(23)), [CLK_BUS_EHCI0] = GATE(0x060, BIT(24)), [CLK_BUS_EHCI1] = GATE(0x060, BIT(25)), @@ -28,6 +30,9 @@ static const struct ccu_clk_gate a64_gates[] = { [CLK_BUS_UART3] = GATE(0x06c, BIT(19)), [CLK_BUS_UART4] = GATE(0x06c, BIT(20)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), [CLK_USB_PHY1] = GATE(0x0cc, BIT(9)), [CLK_USB_HSIC] = GATE(0x0cc, BIT(10)), @@ -44,6 +49,8 @@ static const struct ccu_reset a64_resets[] = { [RST_BUS_MMC0] = RESET(0x2c0, BIT(8)), [RST_BUS_MMC1] = RESET(0x2c0, BIT(9)), [RST_BUS_MMC2] = RESET(0x2c0, BIT(10)), + [RST_BUS_SPI0] = RESET(0x2c0, BIT(20)), + [RST_BUS_SPI1] = RESET(0x2c0, BIT(21)), [RST_BUS_OTG] = RESET(0x2c0, BIT(23)), [RST_BUS_EHCI0] = RESET(0x2c0, BIT(24)), [RST_BUS_EHCI1] = RESET(0x2c0, BIT(25)), diff --git a/drivers/clk/sunxi/clk_a80.c b/drivers/clk/sunxi/clk_a80.c index aec1d80c46..c332888d3c 100644 --- a/drivers/clk/sunxi/clk_a80.c +++ b/drivers/clk/sunxi/clk_a80.c @@ -13,6 +13,16 @@ #include <dt-bindings/reset/sun9i-a80-ccu.h>
static const struct ccu_clk_gate a80_gates[] = { + [CLK_SPI0] = GATE(0x030, BIT(31)), + [CLK_SPI1] = GATE(0x034, BIT(31)), + [CLK_SPI2] = GATE(0x038, BIT(31)), + [CLK_SPI3] = GATE(0x03c, BIT(31)), + + [CLK_BUS_SPI0] = GATE(0x180, BIT(20)), + [CLK_BUS_SPI1] = GATE(0x180, BIT(21)), + [CLK_BUS_SPI2] = GATE(0x180, BIT(22)), + [CLK_BUS_SPI3] = GATE(0x180, BIT(23)), + [CLK_BUS_MMC] = GATE(0x580, BIT(8)),
[CLK_BUS_UART0] = GATE(0x594, BIT(16)), @@ -24,6 +34,11 @@ static const struct ccu_clk_gate a80_gates[] = { };
static const struct ccu_reset a80_resets[] = { + [RST_BUS_SPI0] = RESET(0x1a0, BIT(20)), + [RST_BUS_SPI1] = RESET(0x1a0, BIT(21)), + [RST_BUS_SPI2] = RESET(0x1a0, BIT(22)), + [RST_BUS_SPI3] = RESET(0x1a0, BIT(23)), + [RST_BUS_MMC] = RESET(0x5a0, BIT(8)),
[RST_BUS_UART0] = RESET(0x5b4, BIT(16)), diff --git a/drivers/clk/sunxi/clk_a83t.c b/drivers/clk/sunxi/clk_a83t.c index b5a555da36..36f7e14c45 100644 --- a/drivers/clk/sunxi/clk_a83t.c +++ b/drivers/clk/sunxi/clk_a83t.c @@ -16,6 +16,8 @@ static struct ccu_clk_gate a83t_gates[] = { [CLK_BUS_MMC0] = GATE(0x060, BIT(8)), [CLK_BUS_MMC1] = GATE(0x060, BIT(9)), [CLK_BUS_MMC2] = GATE(0x060, BIT(10)), + [CLK_BUS_SPI0] = GATE(0x060, BIT(20)), + [CLK_BUS_SPI1] = GATE(0x060, BIT(21)), [CLK_BUS_OTG] = GATE(0x060, BIT(24)), [CLK_BUS_EHCI0] = GATE(0x060, BIT(26)), [CLK_BUS_EHCI1] = GATE(0x060, BIT(27)), @@ -27,6 +29,9 @@ static struct ccu_clk_gate a83t_gates[] = { [CLK_BUS_UART3] = GATE(0x06c, BIT(19)), [CLK_BUS_UART4] = GATE(0x06c, BIT(20)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), [CLK_USB_PHY1] = GATE(0x0cc, BIT(9)), [CLK_USB_HSIC] = GATE(0x0cc, BIT(10)), @@ -42,6 +47,8 @@ static struct ccu_reset a83t_resets[] = { [RST_BUS_MMC0] = RESET(0x2c0, BIT(8)), [RST_BUS_MMC1] = RESET(0x2c0, BIT(9)), [RST_BUS_MMC2] = RESET(0x2c0, BIT(10)), + [RST_BUS_SPI0] = RESET(0x2c0, BIT(20)), + [RST_BUS_SPI1] = RESET(0x2c0, BIT(21)), [RST_BUS_OTG] = RESET(0x2c0, BIT(24)), [RST_BUS_EHCI0] = RESET(0x2c0, BIT(26)), [RST_BUS_EHCI1] = RESET(0x2c0, BIT(27)), diff --git a/drivers/clk/sunxi/clk_h3.c b/drivers/clk/sunxi/clk_h3.c index 416aec2b89..5f99ef7342 100644 --- a/drivers/clk/sunxi/clk_h3.c +++ b/drivers/clk/sunxi/clk_h3.c @@ -16,6 +16,8 @@ static struct ccu_clk_gate h3_gates[] = { [CLK_BUS_MMC0] = GATE(0x060, BIT(8)), [CLK_BUS_MMC1] = GATE(0x060, BIT(9)), [CLK_BUS_MMC2] = GATE(0x060, BIT(10)), + [CLK_BUS_SPI0] = GATE(0x060, BIT(20)), + [CLK_BUS_SPI1] = GATE(0x060, BIT(21)), [CLK_BUS_OTG] = GATE(0x060, BIT(23)), [CLK_BUS_EHCI0] = GATE(0x060, BIT(24)), [CLK_BUS_EHCI1] = GATE(0x060, BIT(25)), @@ -31,6 +33,9 @@ static struct ccu_clk_gate h3_gates[] = { [CLK_BUS_UART2] = GATE(0x06c, BIT(18)), [CLK_BUS_UART3] = GATE(0x06c, BIT(19)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), [CLK_USB_PHY1] = GATE(0x0cc, BIT(9)), [CLK_USB_PHY2] = GATE(0x0cc, BIT(10)), @@ -50,6 +55,8 @@ static struct ccu_reset h3_resets[] = { [RST_BUS_MMC0] = RESET(0x2c0, BIT(8)), [RST_BUS_MMC1] = RESET(0x2c0, BIT(9)), [RST_BUS_MMC2] = RESET(0x2c0, BIT(10)), + [RST_BUS_SPI0] = RESET(0x2c0, BIT(20)), + [RST_BUS_SPI1] = RESET(0x2c0, BIT(21)), [RST_BUS_OTG] = RESET(0x2c0, BIT(23)), [RST_BUS_EHCI0] = RESET(0x2c0, BIT(24)), [RST_BUS_EHCI1] = RESET(0x2c0, BIT(25)), diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c index 902612da91..a62d505736 100644 --- a/drivers/clk/sunxi/clk_h6.c +++ b/drivers/clk/sunxi/clk_h6.c @@ -20,6 +20,10 @@ static struct ccu_clk_gate h6_gates[] = { [CLK_BUS_UART1] = GATE(0x90c, BIT(1)), [CLK_BUS_UART2] = GATE(0x90c, BIT(2)), [CLK_BUS_UART3] = GATE(0x90c, BIT(3)), + [CLK_SPI0] = GATE(0x940, BIT(31)), + [CLK_SPI1] = GATE(0x944, BIT(31)), + [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)), + [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)), };
static struct ccu_reset h6_resets[] = { @@ -30,6 +34,8 @@ static struct ccu_reset h6_resets[] = { [RST_BUS_UART1] = RESET(0x90c, BIT(17)), [RST_BUS_UART2] = RESET(0x90c, BIT(18)), [RST_BUS_UART3] = RESET(0x90c, BIT(19)), + [RST_BUS_SPI0] = RESET(0x96c, BIT(16)), + [RST_BUS_SPI1] = RESET(0x96c, BIT(17)), };
static const struct ccu_desc h6_ccu_desc = { diff --git a/drivers/clk/sunxi/clk_r40.c b/drivers/clk/sunxi/clk_r40.c index b9457e1971..92907281f1 100644 --- a/drivers/clk/sunxi/clk_r40.c +++ b/drivers/clk/sunxi/clk_r40.c @@ -17,6 +17,10 @@ static struct ccu_clk_gate r40_gates[] = { [CLK_BUS_MMC1] = GATE(0x060, BIT(9)), [CLK_BUS_MMC2] = GATE(0x060, BIT(10)), [CLK_BUS_MMC3] = GATE(0x060, BIT(11)), + [CLK_BUS_SPI0] = GATE(0x060, BIT(20)), + [CLK_BUS_SPI1] = GATE(0x060, BIT(21)), + [CLK_BUS_SPI2] = GATE(0x060, BIT(22)), + [CLK_BUS_SPI3] = GATE(0x060, BIT(23)), [CLK_BUS_OTG] = GATE(0x060, BIT(25)), [CLK_BUS_EHCI0] = GATE(0x060, BIT(26)), [CLK_BUS_EHCI1] = GATE(0x060, BIT(27)), @@ -34,6 +38,11 @@ static struct ccu_clk_gate r40_gates[] = { [CLK_BUS_UART6] = GATE(0x06c, BIT(22)), [CLK_BUS_UART7] = GATE(0x06c, BIT(23)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_SPI1] = GATE(0x0a4, BIT(31)), + [CLK_SPI2] = GATE(0x0a8, BIT(31)), + [CLK_SPI3] = GATE(0x0ac, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), [CLK_USB_PHY1] = GATE(0x0cc, BIT(9)), [CLK_USB_PHY2] = GATE(0x0cc, BIT(10)), @@ -51,6 +60,10 @@ static struct ccu_reset r40_resets[] = { [RST_BUS_MMC1] = RESET(0x2c0, BIT(9)), [RST_BUS_MMC2] = RESET(0x2c0, BIT(10)), [RST_BUS_MMC3] = RESET(0x2c0, BIT(11)), + [RST_BUS_SPI0] = RESET(0x2c0, BIT(20)), + [RST_BUS_SPI1] = RESET(0x2c0, BIT(21)), + [RST_BUS_SPI2] = RESET(0x2c0, BIT(22)), + [RST_BUS_SPI3] = RESET(0x2c0, BIT(23)), [RST_BUS_OTG] = RESET(0x2c0, BIT(25)), [RST_BUS_EHCI0] = RESET(0x2c0, BIT(26)), [RST_BUS_EHCI1] = RESET(0x2c0, BIT(27)), diff --git a/drivers/clk/sunxi/clk_v3s.c b/drivers/clk/sunxi/clk_v3s.c index c8a9027889..6cc4aabca5 100644 --- a/drivers/clk/sunxi/clk_v3s.c +++ b/drivers/clk/sunxi/clk_v3s.c @@ -17,11 +17,14 @@ static struct ccu_clk_gate v3s_gates[] = { [CLK_BUS_MMC1] = GATE(0x060, BIT(9)), [CLK_BUS_MMC2] = GATE(0x060, BIT(10)), [CLK_BUS_OTG] = GATE(0x060, BIT(24)), + [CLK_BUS_SPI0] = GATE(0x060, BIT(20)),
[CLK_BUS_UART0] = GATE(0x06c, BIT(16)), [CLK_BUS_UART1] = GATE(0x06c, BIT(17)), [CLK_BUS_UART2] = GATE(0x06c, BIT(18)),
+ [CLK_SPI0] = GATE(0x0a0, BIT(31)), + [CLK_USB_PHY0] = GATE(0x0cc, BIT(8)), };
@@ -31,6 +34,7 @@ static struct ccu_reset v3s_resets[] = { [RST_BUS_MMC0] = RESET(0x2c0, BIT(8)), [RST_BUS_MMC1] = RESET(0x2c0, BIT(9)), [RST_BUS_MMC2] = RESET(0x2c0, BIT(10)), + [RST_BUS_SPI0] = RESET(0x2c0, BIT(20)), [RST_BUS_OTG] = RESET(0x2c0, BIT(24)),
[RST_BUS_UART0] = RESET(0x2d8, BIT(16)),

From: Jagan Teki jagan@amarulasolutions.com
Add support for DM_CLK driven clocks and reset gates.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com Signed-off-by: Andre Przywara andre.przywara@arm.com --- drivers/spi/sun4i_spi.c | 83 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 17 deletions(-)
diff --git a/drivers/spi/sun4i_spi.c b/drivers/spi/sun4i_spi.c index 61a5f3522e..c24b834227 100644 --- a/drivers/spi/sun4i_spi.c +++ b/drivers/spi/sun4i_spi.c @@ -19,7 +19,9 @@ */
#include <common.h> +#include <clk.h> #include <dm.h> +#include <reset.h> #include <spi.h> #include <errno.h> #include <fdt_support.h> @@ -114,6 +116,9 @@ struct sun4i_spi_platdata {
struct sun4i_spi_priv { struct sun4i_spi_regs *regs; + struct clk ahb_clk; + struct clk mod_clk; + struct reset_ctl rst_ctl; u32 freq; u32 mode;
@@ -239,23 +244,48 @@ static int sun4i_spi_parse_pins(struct udevice *dev) return 0; }
-static void sun4i_spi_enable_clock(void) +static int sun4i_spi_enable_clock(struct udevice *dev) { - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *const)SUNXI_CCM_BASE; + struct sun4i_spi_priv *priv = dev_get_priv(dev); + int ret; + + ret = clk_enable(&priv->ahb_clk); + if (ret) { + dev_err(dev, "failed to enable ahb clock (ret=%d)\n", ret); + return ret; + } + + ret = clk_enable(&priv->mod_clk); + if (ret) { + dev_err(dev, "failed to enable mod clock (ret=%d)\n", ret); + goto err_ahb; + }
- setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_SPI0)); - writel((1 << 31), &ccm->spi0_clk_cfg); + if (reset_valid(&priv->rst_ctl)) + ret = reset_deassert(&priv->rst_ctl); + if (ret) { + dev_err(dev, "failed to deassert reset gate (ret=%d)\n", ret); + goto err_mod; + } + + return 0; + +err_mod: + clk_disable(&priv->mod_clk); +err_ahb: + clk_disable(&priv->ahb_clk); + + return ret; }
-static void sun4i_spi_disable_clock(void) +static void sun4i_spi_disable_clock(struct udevice *dev) { - 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)); + struct sun4i_spi_priv *priv = dev_get_priv(dev);
+ clk_disable(&priv->ahb_clk); + clk_disable(&priv->mod_clk); + if (reset_valid(&priv->rst_ctl)) + reset_assert(&priv->rst_ctl); }
static int sun4i_spi_ofdata_to_platdata(struct udevice *bus) @@ -274,12 +304,31 @@ static int sun4i_spi_ofdata_to_platdata(struct udevice *bus) return 0; }
-static int sun4i_spi_probe(struct udevice *bus) +static int sun4i_spi_probe(struct udevice *dev) { - struct sun4i_spi_platdata *plat = dev_get_platdata(bus); - struct sun4i_spi_priv *priv = dev_get_priv(bus); + struct sun4i_spi_platdata *plat = dev_get_platdata(dev); + struct sun4i_spi_priv *priv = dev_get_priv(dev); + int ret; + + ret = clk_get_by_name(dev, "ahb", &priv->ahb_clk); + if (ret) { + dev_err(dev, "failed to get ahb clock\n"); + return ret; + } + + ret = clk_get_by_name(dev, "mod", &priv->mod_clk); + if (ret) { + dev_err(dev, "failed to get mod clock\n"); + return ret; + } + + ret = reset_get_by_index(dev, 0, &priv->rst_ctl); + if (ret && ret != -ENOENT) { + dev_err(dev, "failed to get reset gate\n"); + return ret; + }
- sun4i_spi_parse_pins(bus); + sun4i_spi_parse_pins(dev);
priv->regs = (struct sun4i_spi_regs *)(uintptr_t)plat->base_addr; priv->freq = plat->max_hz; @@ -291,7 +340,7 @@ static int sun4i_spi_claim_bus(struct udevice *dev) { struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
- sun4i_spi_enable_clock(); + sun4i_spi_enable_clock(dev->parent);
writel(SUN4I_CTL_ENABLE | SUN4I_CTL_MASTER | SUN4I_CTL_TP | SUN4I_CTL_CS_MANUAL | SUN4I_CTL_CS_ACTIVE_LOW, @@ -309,7 +358,7 @@ static int sun4i_spi_release_bus(struct udevice *dev) reg &= ~SUN4I_CTL_ENABLE; writel(reg, &priv->regs->ctl);
- sun4i_spi_disable_clock(); + sun4i_spi_disable_clock(dev->parent);
return 0; }

From: Oskari Lemmela oskari@lemmela.net
So far the Allwinner SPI driver just covered the older generation SPI peripheral found in the A10 and A20 devices. Newer SoCs use a slightly different device, which is still annoyingly similar: Too similar to justify a separate driver, but too different to have a neatly integrated driver. Embrace the U-Boot spirit and solve this issue with some #ifdefs ;-)
Should the need arise, this can be later turned into runtime decisions, but so far we will never see the need for a joint driver in a certain binary build.
The technical changes were based on arch/arm/mach-sunxi/spl_spi_sunxi.c.
Signed-off-by: Oskari Lemmela oskari@lemmela.net Signed-off-by: Andre Przywara andre.przywara@arm.com --- drivers/spi/Kconfig | 4 +-- drivers/spi/sun4i_spi.c | 94 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 14 deletions(-)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ac7fbab841..05ca2152d8 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -223,9 +223,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 c24b834227..dfbccd9af1 100644 --- a/drivers/spi/sun4i_spi.c +++ b/drivers/spi/sun4i_spi.c @@ -39,6 +39,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) @@ -56,6 +80,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) @@ -94,11 +119,39 @@ #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; @@ -108,6 +161,7 @@ struct sun4i_spi_regs { u32 tc; u32 fifo_sta; }; +#endif
struct sun4i_spi_platdata { u32 base_addr; @@ -154,7 +208,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); @@ -164,7 +218,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) @@ -236,7 +290,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); } @@ -343,9 +400,15 @@ static int sun4i_spi_claim_bus(struct udevice *dev) sun4i_spi_enable_clock(dev->parent);
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; }
@@ -386,10 +449,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... */ @@ -398,16 +461,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"); @@ -480,7 +545,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) @@ -490,7 +555,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; } @@ -504,7 +569,12 @@ static const struct dm_spi_ops sun4i_spi_ops = { };
static const struct udevice_id sun4i_spi_ids[] = { +#ifndef CONFIG_SUNXI_GEN_SUN6I { .compatible = "allwinner,sun4i-a10-spi" }, +#else + { .compatible = "allwinner,sun6i-a31-spi" }, + { .compatible = "allwinner,sun8i-h3-spi" }, +#endif { } };

On Tue, Feb 12, 2019 at 4:11 PM Andre Przywara andre.przywara@arm.com wrote:
We already have a driver for the SPI devices in older Allwinner CPUs. Newer SoCs (SUN6I generation) have a similar, but improved IP block. This series adds support for that, so we can access the SPI flash that some newer boards come with. This series gathers some patches that have appeared on the list in one form or another before (hence the v2 tag), but brings them all together and updates them.
The SUN4I/SUN6I SPI devices share a very similar architecure, but differ annoyingly in quite some details, to a point where a shared driver would look unnecessarily complicated. So we use an #ifdef based approach, which keeps the driver simple, at the cost of not being able to access both types of device in one binary build. But since there are no SoCs which feature both types, this is a theoretical disadvantage.
Patch 1/4 moves the clock toggling from the probe/remove to the claim/release-bus stage. Patch 2/4 adds the SPI gate clocks and reset gates to the clock drivers, also describes at least the enable bit for the SPI mod clock. This is used in patch 3/4, which uses the new DM clock framework in the driver, helping to abstract differences between the SUN4I/SUN6I types. Patch 4/4 eventually adds support for the new SUN6I generation SPI device. The actual device support is determined at compile time, based on the CONFIG_SUNXI_GEN_SUN6I symbol.
What's left out of this series is the config bits needed to actually activate the support for a board. At the moment this is unnecessarily verbose, so that Oskari is looking at simplifying this at the Kconfig level, which would complement this series.
This has been briefly tested on the Pine64-LTS and the OrangePi PC2 board. Please test them if you have other devices with a SUN6I SPI generation.
I have new series for this which I sent two days back [1]. which uses same sun4i driver and implement enum reg set to avoid ifdef.

On Tue, 12 Feb 2019 16:15:22 +0530 Jagan Teki jagan@amarulasolutions.com wrote:
On Tue, Feb 12, 2019 at 4:11 PM Andre Przywara andre.przywara@arm.com wrote:
We already have a driver for the SPI devices in older Allwinner CPUs. Newer SoCs (SUN6I generation) have a similar, but improved IP block. This series adds support for that, so we can access the SPI flash that some newer boards come with. This series gathers some patches that have appeared on the list in one form or another before (hence the v2 tag), but brings them all together and updates them.
The SUN4I/SUN6I SPI devices share a very similar architecure, but differ annoyingly in quite some details, to a point where a shared driver would look unnecessarily complicated. So we use an #ifdef based approach, which keeps the driver simple, at the cost of not being able to access both types of device in one binary build. But since there are no SoCs which feature both types, this is a theoretical disadvantage.
Patch 1/4 moves the clock toggling from the probe/remove to the claim/release-bus stage. Patch 2/4 adds the SPI gate clocks and reset gates to the clock drivers, also describes at least the enable bit for the SPI mod clock. This is used in patch 3/4, which uses the new DM clock framework in the driver, helping to abstract differences between the SUN4I/SUN6I types. Patch 4/4 eventually adds support for the new SUN6I generation SPI device. The actual device support is determined at compile time, based on the CONFIG_SUNXI_GEN_SUN6I symbol.
What's left out of this series is the config bits needed to actually activate the support for a board. At the moment this is unnecessarily verbose, so that Oskari is looking at simplifying this at the Kconfig level, which would complement this series.
This has been briefly tested on the Pine64-LTS and the OrangePi PC2 board. Please test them if you have other devices with a SUN6I SPI generation.
I have new series for this which I sent two days back [1]. which uses same sun4i driver and implement enum reg set to avoid ifdef.
Ah, sorry, I missed that completely. Will have a look at this later on.
Cheers, Andre.
participants (2)
-
Andre Przywara
-
Jagan Teki