mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/6] Initial support for Allwinner A64 SoC
@ 2024-11-07 14:57 Jules Maselbas
  2024-11-07 14:57 ` [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate Jules Maselbas
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

This series adds the initial support for Allwinner, currently only
targetting the Pine64+ board, based on the A64 SoC.

This first series is intented as a second boot image only, that can
be started from u-boot. This series provides the bare minimal so
barebox can load the next OS from sd/mmc.

Jules Maselbas (6):
  clk: clk-composite: fix callback guard in clk_composite_round_rate
  clk: divider: add error code propagation
  clk: Add clock driver for sun50i-a64
  pinctrl: Add sun50i-a64 pinctrl driver
  mci: Add sunxi-mmc driver
  ARM: sunxi: Introduce mach-sunxi

 arch/arm/Kconfig                           |  14 +
 arch/arm/Makefile                          |   1 +
 arch/arm/configs/sunxi_v8_defconfig        |  12 +
 arch/arm/mach-sunxi/Kconfig                |  12 +
 arch/arm/mach-sunxi/Makefile               |   1 +
 arch/arm/mach-sunxi/common.c               |   0
 drivers/clk/Makefile                       |   1 +
 drivers/clk/clk-composite.c                |   2 +-
 drivers/clk/clk-divider.c                  |   5 +-
 drivers/clk/sunxi/Makefile                 |   2 +
 drivers/clk/sunxi/clk-sun50i-a64.c         | 325 +++++++++++
 drivers/clk/sunxi/clk-sun50i-a64.h         |  62 +++
 drivers/mci/Kconfig                        |   6 +
 drivers/mci/Makefile                       |   2 +
 drivers/mci/sunxi-mmc-common.c             | 246 +++++++++
 drivers/mci/sunxi-mmc-pbl.c                |  81 +++
 drivers/mci/sunxi-mmc.c                    | 181 +++++++
 drivers/mci/sunxi-mmc.h                    | 221 ++++++++
 drivers/pinctrl/Kconfig                    |   2 +
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/sunxi/Kconfig              |  13 +
 drivers/pinctrl/sunxi/Makefile             |   3 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 594 +++++++++++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 372 +++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
 include/mach/sunxi/barebox-arm.h           |   7 +
 include/mach/sunxi/xload.h                 |  12 +
 27 files changed, 2400 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/configs/sunxi_v8_defconfig
 create mode 100644 arch/arm/mach-sunxi/Kconfig
 create mode 100644 arch/arm/mach-sunxi/Makefile
 create mode 100644 arch/arm/mach-sunxi/common.c
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h
 create mode 100644 drivers/mci/sunxi-mmc-common.c
 create mode 100644 drivers/mci/sunxi-mmc-pbl.c
 create mode 100644 drivers/mci/sunxi-mmc.c
 create mode 100644 drivers/mci/sunxi-mmc.h
 create mode 100644 drivers/pinctrl/sunxi/Kconfig
 create mode 100644 drivers/pinctrl/sunxi/Makefile
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
 create mode 100644 include/mach/sunxi/barebox-arm.h
 create mode 100644 include/mach/sunxi/xload.h

-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate
  2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
@ 2024-11-07 14:57 ` Jules Maselbas
  2024-11-07 15:05   ` Ahmad Fatoum
  2024-11-08 10:21   ` (subset) " Sascha Hauer
  2024-11-07 14:57 ` [PATCH 2/6] clk: divider: add error code propagation Jules Maselbas
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

In the function clk_composite_round_rate a test for a NULL function pointer
guard the call to the round_rate callback but doesn't check for the correct
callback: it should be round_rate instead of set_rate

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 drivers/clk/clk-composite.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 454bfaeb0c..fdf53ce75e 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -65,7 +65,7 @@ static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
 
 	if (!(hw->clk.flags & CLK_SET_RATE_NO_REPARENT) &&
 	    mux_clk &&
-	    mux_clk->ops->set_rate)
+	    mux_clk->ops->round_rate)
 		return mux_clk->ops->round_rate(clk_to_clk_hw(mux_clk), rate, prate);
 
 	return *prate;
-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 2/6] clk: divider: add error code propagation
  2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
  2024-11-07 14:57 ` [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate Jules Maselbas
@ 2024-11-07 14:57 ` Jules Maselbas
  2024-11-13  8:47   ` Ahmad Fatoum
  2024-11-07 14:57 ` [PATCH 3/6] clk: Add clock driver for sun50i-a64 Jules Maselbas
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

divider_get_val can return a negative error code and should not be used
as a valid divider.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 drivers/clk/clk-divider.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index ccab70aecc..b05b042da5 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -308,6 +308,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 	struct clk *clk = clk_hw_to_clk(hw);
 	struct clk_divider *divider = to_clk_divider(hw);
 	unsigned int value;
+	int ret;
 	u32 val;
 
 	if (divider->flags & CLK_DIVIDER_READ_ONLY)
@@ -320,8 +321,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 		clk_set_rate(clk_get_parent(clk), parent_rate);
 	}
 
-	value = divider_get_val(rate, parent_rate, divider->table,
+	value = ret = divider_get_val(rate, parent_rate, divider->table,
 				divider->width, divider->flags);
+	if (ret < 0)
+		return ret;
 
 	val = readl(divider->reg);
 	val &= ~(clk_div_mask(divider->width) << divider->shift);
-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 3/6] clk: Add clock driver for sun50i-a64
  2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
  2024-11-07 14:57 ` [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate Jules Maselbas
  2024-11-07 14:57 ` [PATCH 2/6] clk: divider: add error code propagation Jules Maselbas
@ 2024-11-07 14:57 ` Jules Maselbas
  2024-11-08 10:39   ` Sascha Hauer
  2024-11-07 14:57 ` [PATCH 4/6] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Clock driver adapted from Linux.
The pll-cpux is set to 816MHz and pll-periph0-2x is set to 1.2GHz.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 drivers/clk/Makefile               |   1 +
 drivers/clk/sunxi/Makefile         |   2 +
 drivers/clk/sunxi/clk-sun50i-a64.c | 325 +++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk-sun50i-a64.h |  62 ++++++
 4 files changed, 390 insertions(+)
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 764539e91e..7d459c298e 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -31,3 +31,4 @@ obj-y				+= bcm/
 obj-$(CONFIG_COMMON_CLK_SCMI)	+= clk-scmi.o
 obj-$(CONFIG_COMMON_CLK_GPIO)   += clk-gpio.o
 obj-$(CONFIG_TI_SCI_CLK)	+= ti-sci-clk.o
+obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644
index 0000000000..4d1dcbebb0
--- /dev/null
+++ b/drivers/clk/sunxi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ARCH_SUN50I_A64) += clk-sun50i-a64.o
diff --git a/drivers/clk/sunxi/clk-sun50i-a64.c b/drivers/clk/sunxi/clk-sun50i-a64.c
new file mode 100644
index 0000000000..c77f366c6e
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun50i-a64.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2022 Jules Maselbas
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+
+#define MHZ (1000UL * 1000UL)
+
+#include "clk-sun50i-a64.h"
+
+#define CCU_PLL_CPUX		0x00
+#define CCU_PLL_PERIPH0		0x28
+#define CCU_CPUX_AXI_CFG	0x50
+
+static struct clk *clks[CLK_NUMBER];
+static struct clk_onecell_data clk_data = {
+	.clks = clks,
+	.clk_num = ARRAY_SIZE(clks),
+};
+
+static inline struct clk *
+sunxi_clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift)
+{
+	return clk_gate(name, parent, reg, shift, 0, 0);
+}
+
+static inline struct clk *
+sunxi_clk_mux(const char *name, const char * const *parents, u8 num_parents,
+	      void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
+}
+
+static inline struct clk *
+sunxi_clk_div_table(const char *name, const char *parent, struct clk_div_table *table,
+	      void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_divider_table(name, parent, CLK_SET_RATE_PARENT, reg, shift,
+				 width, table, 0);
+}
+
+static inline struct clk *
+sunxi_clk_div(const char *name, const char *parent,
+		void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_divider(name, parent, CLK_SET_RATE_PARENT, reg, shift,
+				 width, 0);
+}
+
+static struct clk_div_table div_apb1[] = {
+	{ .val = 0, .div = 2 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 8 },
+	{ /* Sentinel */ },
+};
+
+static struct clk_div_table div_N[] = {
+	{ .val = 0, .div = 1 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 8 },
+	{ /* Sentinel */ },
+};
+
+static const char *sel_cpux[] = {
+	"osc32k",
+	"osc24M",
+	"pll-cpux",
+};
+
+static const char *sel_ahb1[] = {
+	"osc32k",
+	"osc24M",
+	"axi",
+	"pll-periph0",
+};
+
+static const char *sel_apb2[] = {
+	"osc32k",
+	"osc24M",
+	"pll-periph0-2x",
+	"pll-periph0-2x",
+};
+
+static const char *sel_ahb2[] = {
+	"ahb1",
+	"pll-periph0",
+};
+
+static const char *sel_mmc[] = {
+	"osc24M",
+	"pll-periph0-2x",
+	"pll-periph1-2x",
+};
+
+static void sun50i_a64_resets_init(void __iomem *regs)
+{
+	u32 rst;
+
+	rst = 0 |
+		/* RST_USB_PHY0 */ BIT(0) |
+		/* RST_USB_PHY1 */ BIT(1) |
+		/* RST_USB_HSIC */ BIT(2);
+	writel(rst, regs + 0x0cc);
+
+	rst = 0 |
+		/* RST_BUS_MIPI_DSI */ BIT(1) |
+		/* RST_BUS_CE   */ BIT(5) |
+		/* RST_BUS_DMA  */ BIT(6) |
+		/* RST_BUS_MMC0 */ BIT(8) |
+		/* RST_BUS_MMC1 */ BIT(9) |
+		/* RST_BUS_MMC2 */ BIT(10) |
+		/* RST_BUS_NAND */ BIT(13) |
+		/* RST_BUS_DRAM */ BIT(14) |
+		/* RST_BUS_EMAC */ BIT(17) |
+		/* RST_BUS_TS   */ BIT(18) |
+		/* RST_BUS_HSTIMER */ BIT(19) |
+		/* RST_BUS_SPI0  */ BIT(20) |
+		/* RST_BUS_SPI1  */ BIT(21) |
+		/* RST_BUS_OTG   */ BIT(23) |
+		/* RST_BUS_EHCI0 */ BIT(24) |
+		/* RST_BUS_EHCI1 */ BIT(25) |
+		/* RST_BUS_OHCI0 */ BIT(28) |
+		/* RST_BUS_OHCI1 */ BIT(29);
+	writel(rst, regs + 0x2c0);
+
+	rst = 0 |
+		/* RST_BUS_VE    */ BIT(0) |
+		/* RST_BUS_TCON0 */ BIT(3) |
+		/* RST_BUS_TCON1 */ BIT(4) |
+		/* RST_BUS_DEINTERLACE */ BIT(5) |
+		/* RST_BUS_CSI   */ BIT(8) |
+		/* RST_BUS_HDMI0 */ BIT(10) |
+		/* RST_BUS_HDMI1 */ BIT(11) |
+		/* RST_BUS_DE    */ BIT(12) |
+		/* RST_BUS_GPU   */ BIT(20) |
+		/* RST_BUS_MSGBOX   */ BIT(21) |
+		/* RST_BUS_SPINLOCK */ BIT(22) |
+		/* RST_BUS_DBG */ BIT(31);
+	writel(rst, regs + 0x2c4);
+
+	rst = /* RST_BUS_LVDS */ BIT(0);
+	writel(rst, regs + 0x2c8);
+
+	rst = 0 |
+		/* RST_BUS_CODEC */ BIT(0) |
+		/* RST_BUS_SPDIF */ BIT(1) |
+		/* RST_BUS_THS   */ BIT(8) |
+		/* RST_BUS_I2S0  */ BIT(12) |
+		/* RST_BUS_I2S1  */ BIT(13) |
+		/* RST_BUS_I2S2  */ BIT(14);
+	writel(rst, regs + 0x2d0);
+
+	rst = 0 |
+		/* RST_BUS_I2C0 */ BIT(0) |
+		/* RST_BUS_I2C1 */ BIT(1) |
+		/* RST_BUS_I2C2 */ BIT(2) |
+		/* RST_BUS_SCR  */ BIT(5) |
+		/* RST_BUS_UART0 */ BIT(16) |
+		/* RST_BUS_UART1 */ BIT(17) |
+		/* RST_BUS_UART2 */ BIT(18) |
+		/* RST_BUS_UART3 */ BIT(19) |
+		/* RST_BUS_UART4 */ BIT(20);
+	writel(rst, regs + 0x2d8);
+}
+
+static void nop_delay(u32 cnt)
+{
+	while (cnt--)
+		barrier();
+}
+
+static inline void sunxi_clk_set_pll(void __iomem *reg, u32 src, u32 freq)
+{
+	/* NOTE: using u32, max freq is 4GHz
+	 * out freq: src * N * K
+	 * factor N: [1->32]
+	 * factor K: [1->4]
+	 * from the manual: give priority to the choice of K >= 2
+	 */
+	u32 mul = freq / src; /* target multiplier (N * K) */
+	u32 k, n;
+	u32 cfg = BIT(31); /* ENABLE */
+
+	for (k = 4; k > 1; k--) {
+		if ((mul % k) == 0)
+			break;
+	}
+	n = mul / k;
+
+	cfg |= (k - 1) << 4;
+	cfg |= (n - 1) << 8;
+
+	writel(cfg, reg);
+
+	/* wait for pll lock */
+	while (!(readl(reg) & BIT(28)));
+}
+
+static void sun50i_a64_clocks_init(struct device_node *np, void __iomem *regs)
+{
+	sun50i_a64_resets_init(regs);
+
+	/* switch cpu clock source to osc-24M */
+	writel(0x10000, regs + CCU_CPUX_AXI_CFG);
+	/* wait 8 cycles */
+	nop_delay(8);
+	/* set pll-cpux to 816MHz */
+	sunxi_clk_set_pll(regs + CCU_PLL_CPUX, 24 * MHZ, 816 * MHZ);
+	nop_delay(10000); /* HACK: ~1ms delay  */
+	/* switch cpu clock source to pll-cpux*/
+	writel( /* clk_src: 1=24Mhz 2=pll-cpux */ (2 << 16) |
+		/* apb_div: /2 */ (1 << 8) |
+		/* axi_div: /2 */ (1 << 0),
+		regs + CCU_CPUX_AXI_CFG);
+	/* wait 8 cycles */
+	nop_delay(8);
+
+	clks[CLK_PLL_CPUX] = clk_fixed("pll-cpux", 816 * MHZ);
+	clks[CLK_CPUX] = sunxi_clk_mux("cpux", sel_cpux, ARRAY_SIZE(sel_cpux), regs + CCU_CPUX_AXI_CFG, 16, 2);
+
+	/* set pll-periph0-2x to 1.2GHz, as recommended */
+	sunxi_clk_set_pll(regs + CCU_PLL_PERIPH0, 24 * MHZ, 1200 * MHZ);
+
+	clks[CLK_PLL_PERIPH0_2X] = clk_fixed("pll-periph0-2x", 1200 * MHZ);
+	clks[CLK_PLL_PERIPH0]    = clk_fixed_factor("pll-periph0", "pll-periph0-2x", 1, 2, 0);
+
+	clks[CLK_AXI] = sunxi_clk_div("axi", "cpux", regs + CCU_CPUX_AXI_CFG, 0, 2);
+
+	clks[CLK_AHB1] = sunxi_clk_mux("ahb1", sel_ahb1, ARRAY_SIZE(sel_ahb1), regs + 0x054, 12, 2);
+	clks[CLK_AHB2] = sunxi_clk_mux("ahb2", sel_ahb2, ARRAY_SIZE(sel_ahb2), regs + 0x05c, 0, 1);
+
+	clks[CLK_APB1] = sunxi_clk_div_table("apb1", "ahb1", div_apb1, regs + 0x054, 8, 2);
+	clks[CLK_APB2] = sunxi_clk_mux("apb2", sel_apb2, ARRAY_SIZE(sel_apb2), regs + 0x058, 24, 2);
+
+	clks[CLK_BUS_MIPI_DSI] = sunxi_clk_gate("bus-mipi-dsi","ahb1",regs + 0x060, 1);
+	clks[CLK_BUS_CE]    = sunxi_clk_gate("bus-ce",    "ahb1", regs + 0x060, 5);
+	clks[CLK_BUS_DMA]   = sunxi_clk_gate("bus-dma",   "ahb1", regs + 0x060, 6);
+	clks[CLK_BUS_MMC0]  = sunxi_clk_gate("bus-mmc0",  "ahb1", regs + 0x060, 8);
+	clks[CLK_BUS_MMC1]  = sunxi_clk_gate("bus-mmc1",  "ahb1", regs + 0x060, 9);
+	clks[CLK_BUS_MMC2]  = sunxi_clk_gate("bus-mmc2",  "ahb1", regs + 0x060, 10);
+	clks[CLK_BUS_NAND]  = sunxi_clk_gate("bus-nand",  "ahb1", regs + 0x060, 13);
+	clks[CLK_BUS_DRAM]  = sunxi_clk_gate("bus-dram",  "ahb1", regs + 0x060, 14);
+	clks[CLK_BUS_EMAC]  = sunxi_clk_gate("bus-emac",  "ahb2", regs + 0x060, 17);
+	clks[CLK_BUS_TS]    = sunxi_clk_gate("bus-ts",    "ahb1", regs + 0x060, 18);
+	clks[CLK_BUS_HSTIMER] = sunxi_clk_gate("bus-hstimer", "ahb1", regs + 0x060, 19);
+	clks[CLK_BUS_SPI0]  = sunxi_clk_gate("bus-spi0",  "ahb1", regs + 0x060,  20);
+	clks[CLK_BUS_SPI1]  = sunxi_clk_gate("bus-spi1",  "ahb1", regs + 0x060,  21);
+	clks[CLK_BUS_OTG]   = sunxi_clk_gate("bus-otg",   "ahb1", regs + 0x060,  23);
+	clks[CLK_BUS_EHCI0] = sunxi_clk_gate("bus-ehci0", "ahb1", regs + 0x060, 24);
+	clks[CLK_BUS_EHCI1] = sunxi_clk_gate("bus-ehci1", "ahb2", regs + 0x060, 25);
+	clks[CLK_BUS_OHCI0] = sunxi_clk_gate("bus-ohci0", "ahb1", regs + 0x060, 28);
+	clks[CLK_BUS_OHCI1] = sunxi_clk_gate("bus-ohci1", "ahb2", regs + 0x060, 29);
+
+	clks[CLK_BUS_CODEC] = sunxi_clk_gate("bus-codec", "apb1", regs + 0x068, 0);
+	clks[CLK_BUS_SPDIF] = sunxi_clk_gate("bus-spdif", "apb1", regs + 0x068, 1);
+	clks[CLK_BUS_PIO]   = sunxi_clk_gate("bus-pio",   "apb1", regs + 0x068, 5);
+	clks[CLK_BUS_THS]   = sunxi_clk_gate("bus-ths",   "apb1", regs + 0x068, 8);
+	clks[CLK_BUS_I2S0]  = sunxi_clk_gate("bus-i2s0",  "apb1", regs + 0x068, 12);
+	clks[CLK_BUS_I2S1]  = sunxi_clk_gate("bus-i2s1",  "apb1", regs + 0x068, 13);
+	clks[CLK_BUS_I2S2]  = sunxi_clk_gate("bus-i2s2",  "apb1", regs + 0x068, 14);
+
+	clks[CLK_BUS_UART0] = sunxi_clk_gate("bus-uart0", "apb2", regs + 0x06c, 16);
+	clks[CLK_BUS_UART1] = sunxi_clk_gate("bus-uart1", "apb2", regs + 0x06c, 17);
+	clks[CLK_BUS_UART2] = sunxi_clk_gate("bus-uart2", "apb2", regs + 0x06c, 18);
+	clks[CLK_BUS_UART3] = sunxi_clk_gate("bus-uart3", "apb2", regs + 0x06c, 19);
+	clks[CLK_BUS_UART4] = sunxi_clk_gate("bus-uart4", "apb2", regs + 0x06c, 20);
+
+	writel(0, regs + 0x088);
+	clks[CLK_MMC0] = clk_register_composite(
+		"mmc0",	sel_mmc, ARRAY_SIZE(sel_mmc),
+		sunxi_clk_mux("mmc0-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x088, 24, 2),
+		sunxi_clk_div("mmc0-div-m", "mmc0-mux", regs + 0x088, 0, 4),
+		sunxi_clk_gate("mmc0-gate", "mmc0-div-n", regs + 0x088, 31),
+		0);
+
+	writel(0, regs + 0x08c);
+	clks[CLK_MMC1] = clk_register_composite(
+		"mmc1",	sel_mmc, ARRAY_SIZE(sel_mmc),
+		sunxi_clk_mux("mmc1-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x08c, 24, 2),
+		sunxi_clk_div("mmc1-div-m", "mmc1-mux", regs + 0x08c, 0, 4),
+		sunxi_clk_gate("mmc1-gate", "mmc1-div-n", regs + 0x08c, 31),
+		0);
+
+	writel(0, regs + 0x090);
+	clks[CLK_MMC2] = clk_register_composite(
+		"mmc2",	sel_mmc, ARRAY_SIZE(sel_mmc),
+		sunxi_clk_mux("mmc2-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x090, 24, 2),
+		sunxi_clk_div("mmc2-div-m", "mmc2-mux", regs + 0x090, 0, 4),
+		sunxi_clk_gate("mmc2-gate", "mmc2-div-n", regs + 0x090, 31),
+		0);
+}
+
+static int sun50i_a64_ccu_probe(struct device *dev)
+{
+	struct resource *iores;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	sun50i_a64_clocks_init(dev->of_node, IOMEM(iores->start));
+	return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
+}
+
+static __maybe_unused struct of_device_id sun50i_a64_ccu_dt_ids[] = {
+	{
+		.compatible = "allwinner,sun50i-a64-ccu",
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver sun50i_a64_ccu_driver = {
+	.probe	= sun50i_a64_ccu_probe,
+	.name	= "sun50i-a64-ccu",
+	.of_compatible = DRV_OF_COMPAT(sun50i_a64_ccu_dt_ids),
+};
+core_platform_driver(sun50i_a64_ccu_driver);
diff --git a/drivers/clk/sunxi/clk-sun50i-a64.h b/drivers/clk/sunxi/clk-sun50i-a64.h
new file mode 100644
index 0000000000..a4ddc39eb8
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun50i-a64.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+
+#ifndef _CLK_SUN50I_A64_H_
+#define _CLK_SUN50I_A64_H_
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+#include <dt-bindings/reset/sun50i-a64-ccu.h>
+
+#define CLK_OSC_12M			0
+#define CLK_PLL_CPUX			1
+#define CLK_PLL_AUDIO_BASE		2
+#define CLK_PLL_AUDIO			3
+#define CLK_PLL_AUDIO_2X		4
+#define CLK_PLL_AUDIO_4X		5
+#define CLK_PLL_AUDIO_8X		6
+
+/* PLL_VIDEO0 exported for HDMI PHY */
+
+#define CLK_PLL_VIDEO0_2X		8
+#define CLK_PLL_VE			9
+#define CLK_PLL_DDR0			10
+
+/* PLL_PERIPH0 exported for PRCM */
+
+#define CLK_PLL_PERIPH0_2X		12
+#define CLK_PLL_PERIPH1			13
+#define CLK_PLL_PERIPH1_2X		14
+#define CLK_PLL_VIDEO1			15
+#define CLK_PLL_GPU			16
+#define CLK_PLL_MIPI			17
+#define CLK_PLL_HSIC			18
+#define CLK_PLL_DE			19
+#define CLK_PLL_DDR1			20
+#define CLK_AXI				22
+#define CLK_APB				23
+#define CLK_AHB1			24
+#define CLK_APB1			25
+#define CLK_APB2			26
+#define CLK_AHB2			27
+
+/* All the bus gates are exported */
+
+/* The first bunch of module clocks are exported */
+
+#define CLK_USB_OHCI0_12M		90
+
+#define CLK_USB_OHCI1_12M		92
+
+/* All the DRAM gates are exported */
+
+/* And the DSI and GPU module clock is exported */
+
+#define CLK_NUMBER			(CLK_GPU + 1)
+
+#endif /* _CLK_SUN50I_A64_H_ */
-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 4/6] pinctrl: Add sun50i-a64 pinctrl driver
  2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
                   ` (2 preceding siblings ...)
  2024-11-07 14:57 ` [PATCH 3/6] clk: Add clock driver for sun50i-a64 Jules Maselbas
@ 2024-11-07 14:57 ` Jules Maselbas
  2024-11-08 10:48   ` Sascha Hauer
  2024-11-07 14:57 ` [PATCH 5/6] mci: Add sunxi-mmc driver Jules Maselbas
  2024-11-07 14:57 ` [PATCH 6/6] ARM: sunxi: Introduce mach-sunxi Jules Maselbas
  5 siblings, 1 reply; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

sunxi pinctrl driver, adapted from Linux, is split in two parts:
 - pinctrl-sunxi.c that implements gpio, pinctrl and pinmux functions
 - pinctrl-sun50i-a64.c that declare sun50i pins and their functions.
   This file only require minor adaptations and be easily copied from Linux.

The pin functions are needed for uart, SD/MMC, and likely for other
controllers too.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 drivers/pinctrl/Kconfig                    |   2 +
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/sunxi/Kconfig              |  13 +
 drivers/pinctrl/sunxi/Makefile             |   3 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 594 +++++++++++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 372 +++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
 7 files changed, 1209 insertions(+)
 create mode 100644 drivers/pinctrl/sunxi/Kconfig
 create mode 100644 drivers/pinctrl/sunxi/Makefile
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index ee15acdcdb..72ca79aa98 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -106,6 +106,8 @@ config PINCTRL_STM32
 	select HAVE_GPIO_PINCONF
 	help
 	  Pinmux and GPIO controller found on STM32 family
+
+source "drivers/pinctrl/sunxi/Kconfig"
 endif
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f1a5fa5715..3bc718d355 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
 obj-$(CONFIG_PINCTRL_STM32) += pinctrl-stm32.o
 
 obj-$(CONFIG_ARCH_MVEBU) += mvebu/
+obj-$(CONFIG_ARCH_SUNXI) += sunxi/
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
new file mode 100644
index 0000000000..38d8894a7e
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+if ARCH_SUNXI
+
+config PINCTRL_SUNXI
+	bool
+	select GPIOLIB
+
+config PINCTRL_SUN50I_A64
+	bool "Support for Allwinner A64 PIO"
+	default ARCH_SUNXI
+	select PINCTRL_SUNXI
+
+endif
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
new file mode 100644
index 0000000000..db0ff5b50b
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ARCH_SUNXI)		+= pinctrl-sunxi.o
+obj-$(CONFIG_PINCTRL_SUN50I_A64)	+= pinctrl-sun50i-a64.o
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
new file mode 100644
index 0000000000..db96cb52b9
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Allwinner A64 SoCs pinctrl driver.
+ *
+ * Copyright (C) 2016 - ARM Ltd.
+ * Author: Andre Przywara <andre.przywara@arm.com>
+ *
+ * Based on pinctrl-sun7i-a20.c, which is:
+ * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <common.h>
+#include <init.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin a64_pins[] = {
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* MS0 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* CK0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VCCEN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),		/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* DO0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPEN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),		/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* CTS */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* MCLK */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* DI0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPPP */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),		/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* SYNC */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* SYNC */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),		/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* BCLK */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* BCLK */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DATA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),		/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* DOUT */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DOUT */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* RST */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),		/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* DIN */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DIN */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),		/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "uart0"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),		/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "uart0"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),		/* EINT9 */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NWE */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* MOSI */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NALE */
+		  SUNXI_FUNCTION(0x3, "mmc2"),		/* DS */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* MISO */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCLE */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCE1 */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* CS */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0")),	/* NCE0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRE# */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRB0 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CMD */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0")),	/* NRB1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ0 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ1 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ2 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ3 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ4 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ5 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ6 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ7 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQS */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* RST */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D2 */
+		  SUNXI_FUNCTION(0x3, "uart3"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* CS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D3 */
+		  SUNXI_FUNCTION(0x3, "uart3"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* CLK */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* DE */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D4 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* MOSI */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* HSYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D5 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* MISO */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* VSYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D6 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* RTS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D7 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* CTS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D10 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D11 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D12 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD3 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D13 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD2 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D14 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D15 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D18 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D19 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCTL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D20 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP1 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ENULL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D21 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN1 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD3 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D22 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP2 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD2 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D23 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN2 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* CLK */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VPC */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* DE */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VNC */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* HSYNC */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP3 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCTL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* VSYNC */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN3 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ECLKIN */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "pwm"),		/* PWM0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDIO */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* PCK */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* CK */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* ERR */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* HSYNC */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* SYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* VSYNC */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* DVLD */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D0 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D1 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D2 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D3 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D4 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D5 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D6 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D7 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi")),		/* SDA */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "pll"),		/* LOCK_DBG */
+		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SDA */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D1 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* MSI */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D0 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* DI1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CLK */
+		  SUNXI_FUNCTION(0x3, "uart0")),	/* TX */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CMD */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* DO1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D3 */
+		  SUNXI_FUNCTION(0x3, "uart0")),	/* RX */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D2 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* CK1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CMD */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D0 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D1 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D2 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D3 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* RTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* CTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* SYNC */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* SYNC */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* BCLK */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* BCLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),	/* EINT11 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* DOUT */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DOUT */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),	/* EINT12 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* DIN */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DIN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)),	/* EINT13 */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* RTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* CTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "spdif"),		/* OUT */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mic"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mic"),		/* DATA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)),	/* EINT11 */
+};
+
+static const struct sunxi_pinctrl_desc a64_pinctrl_data = {
+	.pins = a64_pins,
+	.npins = ARRAY_SIZE(a64_pins),
+};
+
+static const struct of_device_id a64_pinctrl_dt_match[] = {
+	{
+		.compatible = "allwinner,sun50i-a64-pinctrl",
+		.data = &a64_pinctrl_data
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver a64_pinctrl_driver = {
+	.name		= "sun50i-a64-pinctrl",
+	.probe		= sunxi_pinctrl_probe,
+	.of_compatible	= DRV_OF_COMPAT(a64_pinctrl_dt_match),
+};
+core_platform_driver(a64_pinctrl_driver);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
new file mode 100644
index 0000000000..c52acfe140
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "pinctrl-sunxi: " fmt
+
+#include <common.h>
+#include <io.h>
+#include <of.h>
+#include <of_address.h>
+#include <malloc.h>
+#include <linux/clk.h>
+
+#include "pinctrl-sunxi.h"
+
+/* This driver assumes the gpio function mux value will not change */
+#define FUNC_GPIO_IN	0
+#define FUNC_GPIO_OUT	1
+
+static struct sunxi_pinctrl *to_sunxi_pinctrl(struct pinctrl_device *pdev)
+{
+	return container_of(pdev, struct sunxi_pinctrl, pdev);
+}
+
+static void sunxi_pinctrl_set_pull(struct sunxi_pinctrl *pinctrl,
+				   u16 pin, u32 pull)
+{
+	u32 reg = sunxi_pull_reg(pin);
+	u32 off = sunxi_pull_offset(pin);
+	u32 msk = MUX_PINS_MASK << off;
+	u32 val = readl(pinctrl->base + reg);
+
+	val &= ~msk;
+	val |= (pull << off) & msk;
+	writel(val, pinctrl->base + reg);
+}
+
+static void sunxi_pinctrl_set_dlevel(struct sunxi_pinctrl *pinctrl,
+				   u16 pin, u32 lvl)
+{
+	u32 reg = sunxi_dlevel_reg(pin);
+	u32 off = sunxi_dlevel_offset(pin);
+	u32 msk = MUX_PINS_MASK << off;
+	u32 val = readl(pinctrl->base + reg);
+
+	val &= ~msk;
+	val |= (lvl << off) & msk;
+	writel(val, pinctrl->base + reg);
+}
+
+static void sunxi_pinctrl_set_mux(struct sunxi_pinctrl *pinctrl,
+				  u16 pin, u8 mux)
+{
+	u32 reg = sunxi_mux_reg(pin);
+	u32 off = sunxi_mux_offset(pin);
+	u32 msk = MUX_PINS_MASK << off;
+	u32 val = readl(pinctrl->base + reg);
+
+	val &= ~msk;
+	val |= (mux << off) & msk;
+	writel(val, pinctrl->base + reg);
+}
+
+static u8 sunxi_pinctrl_get_mux(struct sunxi_pinctrl *pinctrl, u16 pin)
+{
+	u32 reg = sunxi_mux_reg(pin);
+	u32 off = sunxi_mux_offset(pin);
+	u32 val = readl(pinctrl->base + reg);
+
+	return (val >> off) & MUX_PINS_MASK;
+}
+
+static void sunxi_pinctrl_set_conf(struct sunxi_pinctrl *pinctrl,
+				  u16 pin, struct device_node *node)
+{
+	u32 val;
+
+	if (of_find_property(node, "bias-pull-up", NULL))
+		sunxi_pinctrl_set_pull(pinctrl, pin, 1);
+	if (of_find_property(node, "bias-pull-down", NULL))
+		sunxi_pinctrl_set_pull(pinctrl, pin, 2);
+	if (of_find_property(node, "bias-disable", NULL))
+		sunxi_pinctrl_set_pull(pinctrl, pin, 0);
+
+	if (!of_property_read_u32(node, "drive-strength", &val)) {
+		val = rounddown(val, 10) / 10 - 1;
+		sunxi_pinctrl_set_dlevel(pinctrl, pin, val);
+	}
+}
+
+static const char *sunxi_pinctrl_parse_function_prop(struct device_node *node)
+{
+	const char *function;
+	int ret;
+
+	/* Try the generic binding */
+	ret = of_property_read_string(node, "function", &function);
+	if (!ret)
+		return function;
+
+	/* And fall back to our legacy one */
+	ret = of_property_read_string(node, "allwinner,function", &function);
+	if (!ret)
+		return function;
+
+	return NULL;
+}
+
+static struct property *sunxi_pinctrl_find_pins_prop(struct device_node *node)
+{
+	struct property *prop;
+
+	/* Try the generic binding */
+	prop = of_find_property(node, "pins", NULL);
+	if (prop)
+		return prop;
+
+	/* And fall back to our legacy one */
+	prop = of_find_property(node, "allwinner,pins", NULL);
+	if (prop)
+		return prop;
+
+	return NULL;
+}
+
+#define sunxi_pinctrl_of_pins_for_each_string(np, prop, s)	\
+	for (prop = sunxi_pinctrl_find_pins_prop(np),		\
+		s = of_prop_next_string(prop, NULL);		\
+		s;						\
+		s = of_prop_next_string(prop, s))
+
+
+static const struct sunxi_desc_pin *
+sunxi_pinctrl_find_pin(struct sunxi_pinctrl *pinctrl, const char *pin_name)
+{
+	const struct sunxi_desc_pin *pin;
+	int i;
+
+	for (i = 0; i < pinctrl->desc->npins; i++) {
+		pin = &pinctrl->desc->pins[i];
+		if (!strcmp(pin->pin.name, pin_name))
+			return pin;
+	}
+
+	return NULL;
+}
+
+static const struct sunxi_desc_function *
+sunxi_pinctrl_find_func(struct sunxi_pinctrl *pinctrl,
+			const char *pin_name, const char *func_name)
+{
+	const struct sunxi_desc_pin *pin;
+	const struct sunxi_desc_function *func;
+
+	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
+	if (!pin)
+		return NULL;
+
+	for (func = pin->functions; func->name; func++)
+		if (!strcmp(func->name, func_name))
+			return func;
+
+	return NULL;
+}
+
+static int sunxi_pinctrl_set_func(struct sunxi_pinctrl *pinctrl,
+				  struct device_node *np,
+				  const char *pin_name, const char *func_name)
+{
+	struct device *dev = pinctrl->pdev.dev;
+	const struct sunxi_desc_pin *pin;
+	const struct sunxi_desc_function *func;
+
+	dev_dbg(dev, "setfunc %s @ %s\n", func_name, pin_name);
+
+	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
+	if (!pin) {
+		dev_err(dev, "pin %s not found\n", pin_name);
+		return -EINVAL;
+	}
+
+	func = sunxi_pinctrl_find_func(pinctrl, pin_name, func_name);
+	if (!func) {
+		dev_err(dev, "func %s not found\n", func_name);
+		return -EINVAL;
+	}
+
+	sunxi_pinctrl_set_mux(pinctrl, pin->pin.number, func->muxval);
+	sunxi_pinctrl_set_conf(pinctrl, pin->pin.number, np);
+
+	return 0;
+}
+
+static int sunxi_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
+{
+	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
+	struct device *dev = pinctrl->pdev.dev;
+	struct property *prop;
+	const char *func_name;
+	const char *pin_name;
+
+	func_name = sunxi_pinctrl_parse_function_prop(np);
+	if (!func_name) {
+		dev_err(dev, "%s: missing 'function' property\n", np->full_name);
+		return -EINVAL;
+	}
+
+	sunxi_pinctrl_of_pins_for_each_string(np, prop, pin_name) {
+		sunxi_pinctrl_set_func(pinctrl, np, pin_name, func_name);
+	}
+
+	return 0;
+}
+
+static int sunxi_pinctrl_set_direction(struct pinctrl_device *pdev, unsigned int gpio, bool in)
+{
+	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
+	u32 func = in ? FUNC_GPIO_IN : FUNC_GPIO_OUT;
+
+	sunxi_pinctrl_set_mux(pinctrl, gpio, func);
+
+	return 0;
+}
+
+static int sunxi_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+	u32 reg = sunxi_data_reg(gpio);
+	u32 bit = sunxi_data_offset(gpio);
+	u32 val = readl(pinctrl->base + reg);
+
+	return val & BIT(bit);
+}
+
+static void sunxi_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+	u32 reg = sunxi_data_reg(gpio);
+	u32 bit = sunxi_data_offset(gpio);
+	u32 val = readl(pinctrl->base + reg);
+
+	if (value)
+		val |= BIT(bit);
+	else
+		val &= ~BIT(bit);
+	writel(val, pinctrl->base + reg);
+}
+
+static int sunxi_gpio_direction_output(struct gpio_chip *chip,
+				       unsigned gpio, int value)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+
+	sunxi_gpio_set(chip, gpio, value);
+	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_OUT);
+
+	return 0;
+}
+
+static int sunxi_gpio_direction_input(struct gpio_chip *chip,
+					unsigned gpio)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+
+	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_IN);
+
+	return 0;
+}
+
+static int sunxi_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+	u32 func = sunxi_pinctrl_get_mux(pinctrl, gpio);
+
+	if (func == FUNC_GPIO_IN)
+		return GPIOF_DIR_IN;
+	if (func == FUNC_GPIO_OUT)
+		return GPIOF_DIR_OUT;
+	return -EINVAL;
+}
+
+static int sunxi_gpio_of_xlate(struct gpio_chip *chip,
+			       const struct of_phandle_args *gpiospec,
+			       u32 *flags)
+{
+	int pin, base;
+
+	if (gpiospec->args_count != 3)
+		return -EINVAL;
+
+	base = PINS_PER_BANK * gpiospec->args[0];
+	pin = base + gpiospec->args[1];
+
+	if (pin > chip->ngpio)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpiospec->args[2];
+
+	return pin;
+}
+
+static struct pinctrl_ops sunxi_pinctrl_ops = {
+	.set_state = sunxi_pinctrl_set_state,
+	.set_direction = sunxi_pinctrl_set_direction,
+};
+
+static struct gpio_ops sunxi_gpio_ops = {
+	.request = sunxi_gpio_direction_input, /* switch to input function */
+	.direction_input = sunxi_gpio_direction_input,
+	.direction_output = sunxi_gpio_direction_output,
+	.get_direction = sunxi_gpio_get_direction,
+	.get = sunxi_gpio_get,
+	.set = sunxi_gpio_set,
+	.of_xlate = sunxi_gpio_of_xlate,
+};
+
+int sunxi_pinctrl_probe(struct device *dev)
+{
+	const struct sunxi_pinctrl_desc *desc;
+	struct sunxi_pinctrl *pinctrl;
+	struct resource *iores;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_PINCTRL))
+		return 0;
+
+	desc = device_get_match_data(dev);
+	if (!desc)
+                return -EINVAL;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	pinctrl = xzalloc(sizeof(*pinctrl));
+	dev->priv = pinctrl;
+	pinctrl->base = IOMEM(iores->start);
+
+	pinctrl->desc = desc;
+	pinctrl->pdev.dev = dev;
+	pinctrl->pdev.ops = &sunxi_pinctrl_ops;
+
+	ret = pinctrl_register(&pinctrl->pdev);
+	if (ret) {
+		dev_err(dev, "couldn't register %s driver\n", "pinctrl");
+		goto err;
+	}
+	dev_dbg(dev, "sunxi %s registered\n", "pinctrl");
+
+	pinctrl->chip.dev = dev;
+	pinctrl->chip.ops = &sunxi_gpio_ops;
+	/* only the first 8 bank are supported */
+	pinctrl->chip.base = 0;
+	pinctrl->chip.ngpio = 8 * PINS_PER_BANK;
+	pinctrl->chip.of_gpio_n_cells = 3;
+
+	if (of_property_read_bool(dev->of_node, "gpio-controller")) {
+		ret = gpiochip_add(&pinctrl->chip);
+		if (ret) {
+			dev_err(dev, "couldn't register %s driver\n", "gpio-chip");
+			goto pinctrl_unregister;
+		}
+		dev_dbg(dev, "sunxi %s registered\n", "gpio-chip");
+	}
+	return 0;
+
+pinctrl_unregister:
+	pinctrl_unregister(&pinctrl->pdev);
+err:
+	release_region(iores);
+	free(pinctrl);
+	return ret;
+}
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
new file mode 100644
index 0000000000..630f1ef98e
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Allwinner A1X SoCs pinctrl driver.
+ *
+ * Copyright (C) 2012 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PINCTRL_SUNXI_H
+#define __PINCTRL_SUNXI_H
+
+#include <pinctrl.h>
+#include <gpio.h>
+
+#define PA_BASE	0
+#define PB_BASE	32
+#define PC_BASE	64
+#define PD_BASE	96
+#define PE_BASE	128
+#define PF_BASE	160
+#define PG_BASE	192
+#define PH_BASE	224
+#define PI_BASE	256
+#define PL_BASE	352
+#define PM_BASE	384
+#define PN_BASE	416
+
+#define SUNXI_PIN_NAME_MAX_LEN	5
+
+#define BANK_MEM_SIZE		0x24
+#define MUX_REGS_OFFSET		0x0
+#define DATA_REGS_OFFSET	0x10
+#define DLEVEL_REGS_OFFSET	0x14
+#define PULL_REGS_OFFSET	0x1c
+
+#define PINS_PER_BANK		32
+#define MUX_PINS_PER_REG	8
+#define MUX_PINS_BITS		4
+#define MUX_PINS_MASK		0x0f
+#define DATA_PINS_PER_REG	32
+#define DATA_PINS_BITS		1
+#define DATA_PINS_MASK		0x01
+#define DLEVEL_PINS_PER_REG	16
+#define DLEVEL_PINS_BITS	2
+#define DLEVEL_PINS_MASK	0x03
+#define PULL_PINS_PER_REG	16
+#define PULL_PINS_BITS		2
+#define PULL_PINS_MASK		0x03
+
+#define GRP_CFG_REG		0x300
+
+#define IO_BIAS_MASK		GENMASK(3, 0)
+
+#define SUN4I_FUNC_INPUT	0
+#define SUN4I_FUNC_IRQ		6
+
+#define PINCTRL_SUN5I_A10S	BIT(1)
+#define PINCTRL_SUN5I_A13	BIT(2)
+#define PINCTRL_SUN5I_GR8	BIT(3)
+#define PINCTRL_SUN6I_A31	BIT(4)
+#define PINCTRL_SUN6I_A31S	BIT(5)
+#define PINCTRL_SUN4I_A10	BIT(6)
+#define PINCTRL_SUN7I_A20	BIT(7)
+#define PINCTRL_SUN8I_R40	BIT(8)
+#define PINCTRL_SUN8I_V3	BIT(9)
+#define PINCTRL_SUN8I_V3S	BIT(10)
+
+#define PIO_POW_MOD_SEL_REG	0x340
+
+enum sunxi_desc_bias_voltage {
+	BIAS_VOLTAGE_NONE,
+	/*
+	 * Bias voltage configuration is done through
+	 * Pn_GRP_CONFIG registers, as seen on A80 SoC.
+	 */
+	BIAS_VOLTAGE_GRP_CONFIG,
+	/*
+	 * Bias voltage is set through PIO_POW_MOD_SEL_REG
+	 * register, as seen on H6 SoC, for example.
+	 */
+	BIAS_VOLTAGE_PIO_POW_MODE_SEL,
+};
+
+struct sunxi_desc_function {
+	const char	*name;
+	u8		muxval;
+};
+
+struct sunxi_desc_pin {
+	struct {
+		const char		name[6];
+		u16			number;
+	} pin;
+	const struct sunxi_desc_function	*functions;
+};
+
+struct sunxi_pinctrl_desc {
+	const struct sunxi_desc_pin	*pins;
+	size_t				npins;
+	unsigned			pin_base;
+	bool				disable_strict_mode;
+	enum sunxi_desc_bias_voltage	io_bias_cfg_variant;
+};
+
+struct sunxi_pinctrl {
+	void __iomem			*base;
+	struct gpio_chip		chip;
+	struct pinctrl_device		pdev;
+	const struct sunxi_pinctrl_desc	*desc;
+};
+
+#define SUNXI_PIN(_pin, ...)					\
+	{							\
+		.pin = _pin,					\
+		.functions = (struct sunxi_desc_function[]){	\
+			__VA_ARGS__, { } },			\
+	}
+
+#define SUNXI_PINCTRL_PIN(bank, pin)				\
+	{							\
+		.name = "P" #bank #pin,				\
+		.number = P ## bank ## _BASE + (pin)		\
+	}
+
+#define SUNXI_FUNCTION(_val, _name)				\
+	{							\
+		.name = _name,					\
+		.muxval = _val,					\
+	}
+
+#define SUNXI_FUNCTION_IRQ_BANK(...)  {}
+
+/*
+ * The sunXi PIO registers are organized as is:
+ * 0x00 - 0x0c	Muxing values.
+ *		8 pins per register, each pin having a 4bits value
+ * 0x10		Pin values
+ *		32 bits per register, each pin corresponding to one bit
+ * 0x14 - 0x18	Drive level
+ *		16 pins per register, each pin having a 2bits value
+ * 0x1c - 0x20	Pull-Up values
+ *		16 pins per register, each pin having a 2bits value
+ *
+ * This is for the first bank. Each bank will have the same layout,
+ * with an offset being a multiple of 0x24.
+ *
+ * The following functions calculate from the pin number the register
+ * and the bit offset that we should access.
+ */
+static inline u32 sunxi_mux_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += MUX_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_mux_offset(u16 pin)
+{
+	u32 pin_num = pin % MUX_PINS_PER_REG;
+	return pin_num * MUX_PINS_BITS;
+}
+
+static inline u32 sunxi_data_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += DATA_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_data_offset(u16 pin)
+{
+	u32 pin_num = pin % DATA_PINS_PER_REG;
+	return pin_num * DATA_PINS_BITS;
+}
+
+static inline u32 sunxi_dlevel_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += DLEVEL_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_dlevel_offset(u16 pin)
+{
+	u32 pin_num = pin % DLEVEL_PINS_PER_REG;
+	return pin_num * DLEVEL_PINS_BITS;
+}
+
+static inline u32 sunxi_pull_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += PULL_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_pull_offset(u16 pin)
+{
+	u32 pin_num = pin % PULL_PINS_PER_REG;
+	return pin_num * PULL_PINS_BITS;
+}
+
+static inline u32 sunxi_grp_config_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+
+	return GRP_CFG_REG + bank * 0x4;
+}
+
+int sunxi_pinctrl_probe(struct device *dev);
+
+#endif /* __PINCTRL_SUNXI_H */
-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 5/6] mci: Add sunxi-mmc driver
  2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
                   ` (3 preceding siblings ...)
  2024-11-07 14:57 ` [PATCH 4/6] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
@ 2024-11-07 14:57 ` Jules Maselbas
  2024-11-07 14:57 ` [PATCH 6/6] ARM: sunxi: Introduce mach-sunxi Jules Maselbas
  5 siblings, 0 replies; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

This driver is adapted from different sources: Linux, u-boot and p-boot.
The latter, p-boot (forked from u-boot), is a bootloader for pinephones.

It currently only support PIO xfer but could be further improved to also
support DMA xfer. This driver is split in three files so it can be used
by the PBL and barebox proper.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 drivers/mci/Kconfig            |   6 +
 drivers/mci/Makefile           |   2 +
 drivers/mci/sunxi-mmc-common.c | 246 +++++++++++++++++++++++++++++++++
 drivers/mci/sunxi-mmc-pbl.c    |  81 +++++++++++
 drivers/mci/sunxi-mmc.c        | 181 ++++++++++++++++++++++++
 drivers/mci/sunxi-mmc.h        | 221 +++++++++++++++++++++++++++++
 include/mach/sunxi/xload.h     |  12 ++
 7 files changed, 749 insertions(+)
 create mode 100644 drivers/mci/sunxi-mmc-common.c
 create mode 100644 drivers/mci/sunxi-mmc-pbl.c
 create mode 100644 drivers/mci/sunxi-mmc.c
 create mode 100644 drivers/mci/sunxi-mmc.h
 create mode 100644 include/mach/sunxi/xload.h

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index f760614725..4641e9cdcd 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -92,6 +92,12 @@ config MCI_DW_PIO
 	help
 	  Use PIO mode (instead of IDMAC) in DW MMC driver.
 
+config MCI_SUNXI_SMHC
+	bool "Allwinner SD-MMC Memory Card Host Controller"
+	help
+	  Enable support for the Allwinner SD-MMC Memory Card Host Controller,
+	  this provides host support for SD and MMC interfaces, in PIO mode.
+
 config MCI_MXS
 	bool "i.MX23/i.MX28"
 	depends on ARCH_MXS
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index d8d7818a48..5e951d695f 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -22,4 +22,6 @@ obj-$(CONFIG_MCI_DW)		+= dw_mmc.o
 obj-$(CONFIG_MCI_DWC_MSHC)	+= dwcmshc-sdhci.o
 obj-$(CONFIG_MCI_MMCI)		+= mmci.o
 obj-$(CONFIG_MCI_STM32_SDMMC2)	+= stm32_sdmmc2.o
+obj-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc.o
+pbl-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc-pbl.o
 obj-pbl-$(CONFIG_MCI_SDHCI)	+= sdhci.o
diff --git a/drivers/mci/sunxi-mmc-common.c b/drivers/mci/sunxi-mmc-common.c
new file mode 100644
index 0000000000..cfb6df8cd5
--- /dev/null
+++ b/drivers/mci/sunxi-mmc-common.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2024 Jules Maselbas
+#include "sunxi-mmc.h"
+
+static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
+{
+	size_t i, len = (data->blocks * data->blocksize) / sizeof(u32);
+	u32 *dst = (u32 *)data->dest;
+
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
+
+	for (i = 0; i < len; i++) {
+		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_empty(host)))
+			return -ETIMEDOUT;
+		dst[i] = sdxc_readl(host, SDXC_REG_FIFO);
+	}
+
+	return 0;
+}
+
+static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
+{
+	size_t i, len = (data->blocks * data->blocksize) / sizeof(u32);
+	u32 *src = (u32 *)data->src;
+
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
+
+	for (i = 0; i < len; i++) {
+		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_full(host)))
+			return -ETIMEDOUT;
+		sdxc_writel(host, SDXC_REG_FIFO, src[i]);
+	}
+
+	return 0;
+}
+
+static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd,
+			      struct mci_data *data, const char **why)
+{
+	const char *err_why = "";
+	u32 cmdval = SDXC_CMD_START;
+	int ret;
+
+	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
+		return -EINVAL;
+
+	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+		return 0; /* using ACMD12 */
+	if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE)
+		cmdval |= SDXC_CMD_SEND_INIT_SEQ;
+
+	if (cmd->resp_type & MMC_RSP_PRESENT)
+		cmdval |= SDXC_CMD_RESP_EXPIRE;
+	if (cmd->resp_type & MMC_RSP_136)
+		cmdval |= SDXC_CMD_LONG_RESPONSE;
+	if (cmd->resp_type & MMC_RSP_CRC)
+		cmdval |= SDXC_CMD_CHK_RESPONSE_CRC;
+
+	/* clear interrupts */
+	sdxc_writel(host, SDXC_REG_RINTR, 0xffffffff);
+
+	if (data) {
+		u32 blksiz = data->blocksize;
+		u32 bytcnt = data->blocks * data->blocksize;
+
+		cmdval |= SDXC_CMD_DATA_EXPIRE;
+		cmdval |= SDXC_CMD_WAIT_PRE_OVER;
+		if (data->flags & MMC_DATA_WRITE)
+			cmdval |= SDXC_CMD_WRITE;
+		if (data->blocks > 1)
+			cmdval |= SDXC_CMD_AUTO_STOP;
+
+		sdxc_writel(host, SDXC_REG_TMOUT, 0xFFFFFF40);
+		sdxc_writel(host, SDXC_REG_BLKSZ, blksiz);
+		sdxc_writel(host, SDXC_REG_BCNTR, bytcnt);
+	}
+
+	sdxc_writel(host, SDXC_REG_CARG, cmd->cmdarg);
+	sdxc_writel(host, SDXC_REG_CMDR, cmdval | cmd->cmdidx);
+	if (data) {
+		if (data->flags & MMC_DATA_WRITE)
+			ret = sdxc_write_data_pio(host, data);
+		else
+			ret = sdxc_read_data_pio(host, data);
+		if (ret < 0) {
+			err_why = "pio error";
+			goto err;
+		}
+	}
+
+	ret = sdxc_xfer_complete(host, 1000 * MSECOND, SDXC_INT_COMMAND_DONE);
+	if (ret) {
+		err_why = "cmd timeout";
+		goto err;
+	}
+
+	if (data) {
+		ret = sdxc_xfer_complete(host, 1000 * MSECOND, data->blocks > 1 ?
+					 SDXC_INT_AUTO_COMMAND_DONE :
+					 SDXC_INT_DATA_OVER);
+		if (ret) {
+			err_why = "data timeout";
+			goto err;
+		}
+	}
+
+	if (cmd->resp_type & MMC_RSP_BUSY) {
+		u32 status;
+		u64 start;
+		start = get_time_ns();
+		do {
+			status = sdxc_readl(host, SDXC_REG_STAS);
+			if (is_timeout(start, 2000 * MSECOND)) {
+				err_why = "resp timeout";
+				ret = -ETIMEDOUT;
+				goto err;
+			}
+		} while (status & SDXC_STATUS_BUSY);
+	}
+
+	if (wait_on_timeout(1000 * MSECOND, !sdxc_is_card_busy(host))) {
+		err_why = "card busy timeout";
+		ret = -ETIMEDOUT;
+		goto err;
+	}
+
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[0] = sdxc_readl(host, SDXC_REG_RESP3);
+		cmd->response[1] = sdxc_readl(host, SDXC_REG_RESP2);
+		cmd->response[2] = sdxc_readl(host, SDXC_REG_RESP1);
+		cmd->response[3] = sdxc_readl(host, SDXC_REG_RESP0);
+	} else if (cmd->resp_type & MMC_RSP_PRESENT) {
+		cmd->response[0] = sdxc_readl(host, SDXC_REG_RESP0);
+	}
+
+err:
+	if (why)
+		*why = err_why;
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_FIFO_RESET);
+	return ret;
+}
+
+static int sunxi_mmc_update_clk(struct sunxi_mmc_host *host)
+{
+	u32 cmdval;
+
+	cmdval = SDXC_CMD_START |
+	         SDXC_CMD_UPCLK_ONLY |
+	         SDXC_CMD_WAIT_PRE_OVER;
+
+	sdxc_writel(host, SDXC_REG_CARG, 0);
+	sdxc_writel(host, SDXC_REG_CMDR, cmdval);
+
+	if (wait_on_timeout(2000 * MSECOND, !(sdxc_readl(host, SDXC_REG_CMDR) & SDXC_CMD_START)))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int sunxi_mmc_setup_clk(struct sunxi_mmc_host *host, u32 freq)
+{
+	u32 val, div, sclk;
+	int ret;
+
+	sclk = host->clkrate;
+	if (sclk == 0)
+		return -EINVAL;
+
+	/* disable card clock */
+	val = sdxc_readl(host, SDXC_REG_CLKCR);
+	val &= ~(SDXC_CLK_ENABLE | SDXC_CLK_LOW_POWER_ON);
+	val |= SDXC_CLK_MASK_DATA0;
+	sdxc_writel(host, SDXC_REG_CLKCR, val);
+
+	ret = sunxi_mmc_update_clk(host);
+	if (ret)
+		return ret;
+
+	/*
+	 * Configure the controller to use the new timing mode if needed.
+	 * On controllers that only support the new timing mode, such as
+	 * the eMMC controller on the A64, this register does not exist,
+	 * and any writes to it are ignored.
+	 */
+	if (host->cfg->needs_new_timings) {
+		/* Don't touch the delay bits */
+		val = sdxc_readl(host, SDXC_REG_NTSR);
+		val |= SDXC_NTSR_2X_TIMING_MODE;
+		sdxc_writel(host, SDXC_REG_NTSR, val);
+	}
+
+	/* setup clock rate */
+	div = DIV_ROUND_UP(sclk, freq);
+	div /= 2; /* divisor is multiplied by 2 */
+	if (div > 255)
+		div = 255;
+
+	/* set internal divider */
+	val = sdxc_readl(host, SDXC_REG_CLKCR);
+	val &= ~SDXC_CLK_DIVIDER_MASK;
+	val |= div;
+	sdxc_writel(host, SDXC_REG_CLKCR, val);
+
+	/* enable card clock */
+	val = sdxc_readl(host, SDXC_REG_CLKCR);
+	val |= SDXC_CLK_ENABLE;
+	val &= ~SDXC_CLK_MASK_DATA0;
+	sdxc_writel(host, SDXC_REG_CLKCR, val);
+
+	return sunxi_mmc_update_clk(host);
+}
+
+static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios)
+{
+	int ret = 0;
+	u32 width;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_8:
+		width = SDXC_WIDTH_8BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		width = SDXC_WIDTH_4BIT;
+		break;
+	default:
+		width = SDXC_WIDTH_1BIT;
+		break;
+	}
+	sdxc_writel(host, SDXC_REG_WIDTH, width);
+
+	if (ios->clock)
+		ret = sunxi_mmc_setup_clk(host, ios->clock);
+	return ret;
+}
+
+static void sunxi_mmc_init(struct sunxi_mmc_host *host)
+{
+	/* Reset controller */
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_RESET);
+	udelay(1000);
+
+	sdxc_writel(host, SDXC_REG_RINTR, 0xffffffff);
+	sdxc_writel(host, SDXC_REG_IMASK, 0);
+
+	sdxc_writel(host, SDXC_REG_TMOUT, 0xffffff40);
+}
diff --git a/drivers/mci/sunxi-mmc-pbl.c b/drivers/mci/sunxi-mmc-pbl.c
new file mode 100644
index 0000000000..deeb2fa142
--- /dev/null
+++ b/drivers/mci/sunxi-mmc-pbl.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2024 Jules Maselbas
+#include <common.h>
+
+#include <mach/sunxi/xload.h>
+#include "sunxi-mmc.h"
+#include "sunxi-mmc-common.c"
+
+#define SECTOR_SIZE			512
+#define SUPPORT_MAX_BLOCKS		16U
+
+static int sunxi_mmc_read_block(struct sunxi_mmc_host *host,
+				void *dst, unsigned int blocknum,
+				unsigned int blocks)
+{
+	struct mci_data data;
+	struct mci_cmd cmd = {
+		.cmdidx = (blocks > 1) ? MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK,
+		 /* mci->high_capacity ? blocknum : blocknum * mci->read_bl_len, */
+		 /* TODO: detect if card is high-capacity */
+		.cmdarg = blocknum,
+		.resp_type = MMC_RSP_R1,
+	};
+	int ret;
+
+	data.dest = dst;
+	data.blocks = blocks;
+	data.blocksize = SECTOR_SIZE; /* compat with MMC/SD */
+	data.flags = MMC_DATA_READ;
+
+	ret = sunxi_mmc_send_cmd(host, &cmd, &data, NULL);
+
+	if (ret || blocks > 1) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = MMC_RSP_R1b;
+		sunxi_mmc_send_cmd(host, &cmd, NULL, NULL);
+	}
+
+	return ret;
+}
+
+static int sunxi_mmc_bio_read(struct pbl_bio *bio, off_t start,
+				void *buf, unsigned int nblocks)
+{
+	struct sunxi_mmc_host *host = bio->priv;
+	unsigned int count = 0;
+	unsigned int block_len = SECTOR_SIZE;
+	int ret;
+
+	while (count < nblocks) {
+		unsigned int n = min_t(unsigned int, nblocks - count, SUPPORT_MAX_BLOCKS);
+
+		ret = sunxi_mmc_read_block(host, buf, start, n);
+		if (ret < 0)
+			return ret;
+
+		count += n;
+		start += n;
+		buf += n * block_len;
+	}
+
+	return count;
+}
+
+int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
+		       unsigned int clock, unsigned int slot)
+{
+	static struct sunxi_mmc_host host;
+	struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_4, .clock = 400000 };
+
+	host.base = base;
+	host.clkrate = clock / 2;
+	bio->priv = &host;
+	bio->read = sunxi_mmc_bio_read;
+
+	sunxi_mmc_init(&host);
+	sunxi_mmc_set_ios(&host, &ios);
+
+	return 0;
+}
diff --git a/drivers/mci/sunxi-mmc.c b/drivers/mci/sunxi-mmc.c
new file mode 100644
index 0000000000..0f67c198f6
--- /dev/null
+++ b/drivers/mci/sunxi-mmc.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2024 Jules Maselbas
+// derived from: linux/drivers/mmc/host/sunxi-mmc.c
+#define pr_fmt(fmt) "sunxi-mmc: " fmt
+
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <init.h>
+#include <mci.h>
+
+#include <gpio.h>
+#include <of_gpio.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <errno.h>
+#include <dma.h>
+
+#include "sunxi-mmc.h"
+#include "sunxi-mmc-common.c"
+
+static int sdxc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+	struct device *dev = mci->hw_dev;
+	const char *why;
+	int ret;
+
+	ret = sunxi_mmc_send_cmd(host, cmd, data, &why);
+	if (ret && ret != -ETIMEDOUT)
+		dev_err(dev, "error %s CMD%d (%d)\n", why, cmd->cmdidx, ret);
+	if (ret == -ETIMEDOUT)
+		mdelay(1);
+
+	return ret;
+}
+
+static void sdxc_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+	struct device *dev = mci->hw_dev;
+
+	dev_dbg(dev, "buswidth: %d clock: %d\n", 1 << ios->bus_width, ios->clock);
+	sunxi_mmc_set_ios(host, ios);
+}
+
+static int sdxc_card_present(struct mci_host *mci)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+	struct device *dev = mci->hw_dev;
+
+	/* No gpio, assume card is present */
+	if (!gpio_is_valid(host->gpio_cd)) {
+		dev_err(dev, "card detect fail: gpio not valid\n");
+		return 1;
+	}
+
+	return gpio_get_value(host->gpio_cd) ? 0 : 1;
+}
+
+static int sdxc_init(struct mci_host *mci, struct device *dev)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+
+	sunxi_mmc_init(host);
+
+	return 0;
+}
+
+static const struct mci_ops sdxc_mmc_ops = {
+	.send_cmd = sdxc_send_cmd,
+	.set_ios = sdxc_set_ios,
+	.init = sdxc_init,
+	.card_present = sdxc_card_present,
+};
+
+static int sunxi_mmc_probe(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct resource *iores;
+	struct sunxi_mmc_host *host;
+	unsigned int f_min, f_max;
+	int ret;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+	host = xzalloc(sizeof(*host));
+	host->base = IOMEM(iores->start);
+	dma_set_mask(dev, DMA_BIT_MASK(32));
+	host->cfg = device_get_match_data(dev);
+
+	host->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
+
+	host->clk_ahb = clk_get(dev, "ahb");
+	if (IS_ERR(host->clk_ahb)) {
+		ret = PTR_ERR(host->clk_ahb);
+		goto err;
+	}
+
+	host->clk_mmc = clk_get(dev, "mmc");
+	if (IS_ERR(host->clk_mmc)) {
+		ret = PTR_ERR(host->clk_mmc);
+		goto err;
+	}
+
+	clk_enable(host->clk_ahb);
+	clk_enable(host->clk_mmc);
+
+	host->mci.hw_dev = dev;
+	host->mci.ops = sdxc_mmc_ops;
+	host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA
+		| MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED
+		| MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+	/* source clock is pre-divided by 2 */
+	host->clkrate = clk_get_rate(host->clk_mmc) / 2;
+	f_min = host->clkrate / 510;
+	f_max = host->clkrate;
+	/* clock must at least support freq as low as 400K, and reach 52M */
+	if (400000 < f_min || f_max < 52000000) {
+		/* if not, try to get a better clock, target rate of 2 * 52MHz,
+		 * 400KHz can be acheived with a divisor of 130, which is well
+		 * within range.
+		 */
+		clk_set_rate(host->clk_mmc, 2 * 260 * 400000);
+		host->clkrate = clk_get_rate(host->clk_mmc) / 2;
+		f_min = host->clkrate / 510;
+		f_max = host->clkrate;
+	}
+	dev_dbg(dev, "freq: min %d max %d\n", f_min, f_max);
+	mci_of_parse(&host->mci);
+	host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA
+		| MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED
+		| MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+	f_min = min_t(unsigned int,   400000, f_min);
+	f_max = min_t(unsigned int, 52000000, f_max);
+	host->mci.f_min = max_t(unsigned int, host->mci.f_min, f_min);
+	host->mci.f_max = min_t(unsigned int, host->mci.f_max, f_max);
+
+	return mci_register(&host->mci);
+err:
+	if (host->clk_mmc)
+		clk_put(host->clk_mmc);
+	if (host->clk_ahb)
+		clk_put(host->clk_ahb);
+	free(host);
+	release_region(iores);
+	return ret;
+}
+
+static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
+	.idma_des_size_bits = 16,
+	.clk_delays = NULL,
+	.can_calibrate = true,
+	.mask_data0 = true,
+	.needs_new_timings = true,
+};
+
+static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
+	.idma_des_size_bits = 13,
+	.clk_delays = NULL,
+	.can_calibrate = true,
+	.needs_new_timings = true,
+};
+
+static __maybe_unused struct of_device_id sunxi_mmc_compatible[] = {
+	{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
+	{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
+	{ /* sentinel */ }
+};
+
+static struct driver sunxi_mmc_driver = {
+	.name  = "sunxi-mmc",
+	.probe = sunxi_mmc_probe,
+	.of_compatible = DRV_OF_COMPAT(sunxi_mmc_compatible),
+};
+device_platform_driver(sunxi_mmc_driver);
diff --git a/drivers/mci/sunxi-mmc.h b/drivers/mci/sunxi-mmc.h
new file mode 100644
index 0000000000..2f6193d0af
--- /dev/null
+++ b/drivers/mci/sunxi-mmc.h
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2024 Jules Maselbas */
+/* derived from: linux/drivers/mmc/host/sunxi-mmc.c */
+
+#ifndef SUNXI_MMC_H
+#define SUNXI_MMC_H
+
+#include <io.h>
+#include <linux/bitops.h>
+#include <clock.h>
+#include <mci.h>
+
+#define SDXC_REG_GCTRL	(0x00) /* Global Control */
+#define SDXC_REG_CLKCR	(0x04) /* Clock Control */
+#define SDXC_REG_TMOUT	(0x08) /* Time Out */
+#define SDXC_REG_WIDTH	(0x0C) /* Bus Width */
+#define SDXC_REG_BLKSZ	(0x10) /* Block Size */
+#define SDXC_REG_BCNTR	(0x14) /* Byte Count */
+#define SDXC_REG_CMDR	(0x18) /* Command */
+#define SDXC_REG_CARG	(0x1C) /* Argument */
+#define SDXC_REG_RESP0	(0x20) /* Response 0 */
+#define SDXC_REG_RESP1	(0x24) /* Response 1 */
+#define SDXC_REG_RESP2	(0x28) /* Response 2 */
+#define SDXC_REG_RESP3	(0x2C) /* Response 3 */
+#define SDXC_REG_IMASK	(0x30) /* Interrupt Mask */
+#define SDXC_REG_MISTA	(0x34) /* Masked Interrupt Status */
+#define SDXC_REG_RINTR	(0x38) /* Raw Interrupt Status */
+#define SDXC_REG_STAS	(0x3C) /* Status */
+#define SDXC_REG_FTRGL	(0x40) /* FIFO Threshold Watermark */
+#define SDXC_REG_FUNS	(0x44) /* Function Select */
+#define SDXC_REG_CBCR	(0x48) /* CIU Byte Count */
+#define SDXC_REG_BBCR	(0x4C) /* BIU Byte Count */
+#define SDXC_REG_DBGC	(0x50) /* Debug Enable */
+#define SDXC_REG_A12A	(0x58) /* Auto Command 12 Argument */
+#define SDXC_REG_NTSR	(0x5C) /* SMC New Timing Set Register */
+#define SDXC_REG_HWRST	(0x78) /* Card Hardware Reset */
+#define SDXC_REG_DMAC	(0x80) /* IDMAC Control */
+#define SDXC_REG_DLBA	(0x84) /* IDMAC Descriptor List Base Addresse */
+#define SDXC_REG_IDST	(0x88) /* IDMAC Status */
+#define SDXC_REG_IDIE	(0x8C) /* IDMAC Interrupt Enable */
+#define SDXC_REG_CHDA	(0x90)
+#define SDXC_REG_CBDA	(0x94)
+
+#define SDXC_REG_DRV_DL		0x140 /* Drive Delay Control Register */
+#define SDXC_REG_SAMP_DL_REG	0x144 /* SMC sample delay control */
+#define SDXC_REG_DS_DL_REG	0x148 /* SMC data strobe delay control */
+
+#define SDXC_REG_FIFO	(0x200) /* FIFO */
+
+#define SDXC_GCTRL_SOFT_RESET		BIT(0)
+#define SDXC_GCTRL_FIFO_RESET		BIT(1)
+#define SDXC_GCTRL_DMA_RESET		BIT(2)
+#define SDXC_GCTRL_RESET \
+	(SDXC_GCTRL_SOFT_RESET | SDXC_GCTRL_FIFO_RESET | SDXC_GCTRL_DMA_RESET)
+#define SDXC_GCTRL_DMA_ENABLE		BIT(5)
+#define SDXC_GCTRL_ACCESS_BY_AHB	BIT(31)
+
+#define SDXC_CMD_RESP_EXPIRE		BIT(6)
+#define SDXC_CMD_LONG_RESPONSE		BIT(7)
+#define SDXC_CMD_CHK_RESPONSE_CRC	BIT(8)
+#define SDXC_CMD_DATA_EXPIRE		BIT(9)
+#define SDXC_CMD_WRITE			BIT(10)
+#define SDXC_CMD_AUTO_STOP		BIT(12)
+#define SDXC_CMD_WAIT_PRE_OVER		BIT(13)
+#define SDXC_CMD_ABORT_STOP		BIT(14)
+#define SDXC_CMD_SEND_INIT_SEQ		BIT(15)
+#define SDXC_CMD_UPCLK_ONLY		BIT(21)
+#define SDXC_CMD_START			BIT(31)
+
+#define SDXC_NTSR_2X_TIMING_MODE	BIT(31)
+
+/* clock control bits */
+#define SDXC_CLK_MASK_DATA0	BIT(31)
+#define SDXC_CLK_LOW_POWER_ON	BIT(17)
+#define SDXC_CLK_ENABLE		BIT(16)
+#define SDXC_CLK_DIVIDER_MASK	(0xff)
+
+/* bus width */
+#define SDXC_WIDTH_1BIT	0
+#define SDXC_WIDTH_4BIT	BIT(0)
+#define SDXC_WIDTH_8BIT	BIT(1)
+
+/* interrupt bits */
+#define SDXC_INT_RESP_ERROR		BIT(1)
+#define SDXC_INT_COMMAND_DONE		BIT(2)
+#define SDXC_INT_DATA_OVER		BIT(3)
+#define SDXC_INT_TX_DATA_REQUEST	BIT(4)
+#define SDXC_INT_RX_DATA_REQUEST	BIT(5)
+#define SDXC_INT_RESP_CRC_ERROR		BIT(6)
+#define SDXC_INT_DATA_CRC_ERROR		BIT(7)
+#define SDXC_INT_RESP_TIMEOUT		BIT(8)
+#define SDXC_INT_DATA_TIMEOUT		BIT(9)
+#define SDXC_INT_VOLTAGE_CHANGE_DONE	BIT(10)
+#define SDXC_INT_FIFO_RUN_ERROR		BIT(11)
+#define SDXC_INT_HARD_WARE_LOCKED	BIT(12)
+#define SDXC_INT_START_BIT_ERROR	BIT(13)
+#define SDXC_INT_AUTO_COMMAND_DONE	BIT(14)
+#define SDXC_INT_END_BIT_ERROR		BIT(15)
+#define SDXC_INT_SDIO_INTERRUPT		BIT(16)
+#define SDXC_INT_CARD_INSERT		BIT(30)
+#define SDXC_INT_CARD_REMOVE		BIT(31)
+#define SDXC_INTERRUPT_ERROR_BIT	\
+	(SDXC_INT_RESP_ERROR |		\
+	 SDXC_INT_RESP_CRC_ERROR |	\
+	 SDXC_INT_DATA_CRC_ERROR |	\
+	 SDXC_INT_RESP_TIMEOUT |	\
+	 SDXC_INT_DATA_TIMEOUT |	\
+	 SDXC_INT_HARD_WARE_LOCKED |	\
+	 SDXC_INT_START_BIT_ERROR |	\
+	 SDXC_INT_END_BIT_ERROR)
+
+#define SDXC_INTERRUPT_DONE_BIT		\
+	(SDXC_INT_AUTO_COMMAND_DONE |	\
+	 SDXC_INT_DATA_OVER |		\
+	 SDXC_INT_COMMAND_DONE |	\
+	 SDXC_INT_VOLTAGE_CHANGE_DONE)
+
+/* status */
+#define SDXC_STATUS_FIFO_EMPTY		BIT(2)
+#define SDXC_STATUS_FIFO_FULL		BIT(3)
+#define SDXC_STATUS_CARD_PRESENT	BIT(8)
+#define SDXC_STATUS_BUSY		BIT(9)
+
+struct sunxi_mmc_clk_delay {
+	u32 output;
+	u32 sample;
+};
+
+struct sunxi_mmc_cfg {
+	u32 idma_des_size_bits;
+	u32 idma_des_shift;
+	const struct sunxi_mmc_clk_delay *clk_delays;
+
+	/* does the IP block support autocalibration? */
+	bool can_calibrate;
+
+	/* Does DATA0 needs to be masked while the clock is updated */
+	bool mask_data0;
+
+	/*
+	 * hardware only supports new timing mode, either due to lack of
+	 * a mode switch in the clock controller, or the mmc controller
+	 * is permanently configured in the new timing mode, without the
+	 * NTSR mode switch.
+	 */
+	bool needs_new_timings;
+
+	/* clock hardware can switch between old and new timing modes */
+	bool ccu_has_timings_switch;
+};
+
+struct sunxi_mmc_host {
+	struct mci_host mci;
+	struct device *dev;
+	struct clk *clk_ahb, *clk_mmc;
+	void __iomem *base;
+	int gpio_cd;
+
+	const struct sunxi_mmc_cfg *cfg;
+	u32 clkrate;
+};
+
+static inline struct sunxi_mmc_host *to_sunxi_mmc_host(struct mci_host *mci)
+{
+	return container_of(mci, struct sunxi_mmc_host, mci);
+}
+
+static inline u32 sdxc_readl(struct sunxi_mmc_host *host, u32 reg)
+{
+	return readl(host->base + reg);
+}
+
+static inline void sdxc_writel(struct sunxi_mmc_host *host, u32 reg, u32 val)
+{
+	writel(val, host->base + reg);
+}
+
+static inline int sdxc_is_fifo_empty(struct sunxi_mmc_host *host)
+{
+	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_FIFO_EMPTY;
+}
+
+static inline int sdxc_is_fifo_full(struct sunxi_mmc_host *host)
+{
+	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_FIFO_FULL;
+}
+
+static inline int sdxc_is_card_busy(struct sunxi_mmc_host *host)
+{
+	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_BUSY;
+}
+
+#ifdef __PBL__
+/*
+ * Stubs to make timeout logic below work in PBL
+ */
+#define get_time_ns()		0
+/*
+ * Use time in us as a busy counter timeout value
+ */
+#define is_timeout(s, t)	((s)++ > ((t) / 1000))
+#endif
+
+static inline int sdxc_xfer_complete(struct sunxi_mmc_host *host, u64 timeout, u32 flags)
+{
+	u64 start;
+	u32 rint;
+
+	start = get_time_ns();
+	do {
+		rint = sdxc_readl(host, SDXC_REG_RINTR);
+		if (rint & SDXC_INTERRUPT_ERROR_BIT)
+			break;
+		if (rint & flags)
+			return 0;
+	} while (!is_timeout(start, timeout));
+
+	return -ETIMEDOUT;
+}
+
+#endif
diff --git a/include/mach/sunxi/xload.h b/include/mach/sunxi/xload.h
new file mode 100644
index 0000000000..f978db4951
--- /dev/null
+++ b/include/mach/sunxi/xload.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_XLOAD_H
+#define __MACH_XLOAD_H
+
+#include <linux/compiler.h>
+#include <pbl/bio.h>
+
+int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
+		       unsigned int clock, unsigned int slot);
+
+#endif
-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 6/6] ARM: sunxi: Introduce mach-sunxi
  2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
                   ` (4 preceding siblings ...)
  2024-11-07 14:57 ` [PATCH 5/6] mci: Add sunxi-mmc driver Jules Maselbas
@ 2024-11-07 14:57 ` Jules Maselbas
  5 siblings, 0 replies; 16+ messages in thread
From: Jules Maselbas @ 2024-11-07 14:57 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Add some boilerplate, Makefiles, Kbuild.
A generic barebox-dt-2nd.img image can be build for A64 SoC using
the sunxi_v8_defconfig.

This image can be booted from u-boot and has been tested on Pine64+,
it should work on other A64 SoC such as the pinephone, and other Pine64
boards, it might also work on H6 SoC which uses the same sd/mmc controler.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 arch/arm/Kconfig                    | 14 ++++++++++++++
 arch/arm/Makefile                   |  1 +
 arch/arm/configs/sunxi_v8_defconfig | 12 ++++++++++++
 arch/arm/mach-sunxi/Kconfig         | 12 ++++++++++++
 arch/arm/mach-sunxi/Makefile        |  1 +
 arch/arm/mach-sunxi/common.c        |  0
 include/mach/sunxi/barebox-arm.h    |  7 +++++++
 7 files changed, 47 insertions(+)
 create mode 100644 arch/arm/configs/sunxi_v8_defconfig
 create mode 100644 arch/arm/mach-sunxi/Kconfig
 create mode 100644 arch/arm/mach-sunxi/Makefile
 create mode 100644 arch/arm/mach-sunxi/common.c
 create mode 100644 include/mach/sunxi/barebox-arm.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9b90c8009a..fa163f182c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -109,6 +109,19 @@ config ARCH_SOCFPGA
 	select CPU_V7
 	select COMMON_CLK
 
+config ARCH_SUNXI
+	bool "Allwinner SoCs"
+	depends on 64BIT
+	select CLKDEV_LOOKUP
+	select COMMON_CLK
+	select COMMON_CLK_OF_PROVIDER
+	select GPIOLIB
+	select HAVE_PBL_MULTI_IMAGES
+	select OFDEVICE
+	select OFTREE
+	select PINCTRL
+	select RELOCATABLE
+
 config ARCH_TEGRA
 	bool "NVIDIA Tegra"
 	depends on 32BIT
@@ -256,6 +269,7 @@ source "arch/arm/mach-omap/Kconfig"
 source "arch/arm/mach-pxa/Kconfig"
 source "arch/arm/mach-rockchip/Kconfig"
 source "arch/arm/mach-socfpga/Kconfig"
+source "arch/arm/mach-sunxi/Kconfig"
 source "arch/arm/mach-stm32mp/Kconfig"
 source "arch/arm/mach-versatile/Kconfig"
 source "arch/arm/mach-vexpress/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6785432c52..d562892a98 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -96,6 +96,7 @@ machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip
 machine-$(CONFIG_ARCH_SAMSUNG)		+= samsung
 machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga
 machine-$(CONFIG_ARCH_STM32MP)		+= stm32mp
+machine-$(CONFIG_ARCH_SUNXI)		+= sunxi
 machine-$(CONFIG_ARCH_VERSATILE)	+= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		+= vexpress
 machine-$(CONFIG_ARCH_TEGRA)		+= tegra
diff --git a/arch/arm/configs/sunxi_v8_defconfig b/arch/arm/configs/sunxi_v8_defconfig
new file mode 100644
index 0000000000..ebadd1c950
--- /dev/null
+++ b/arch/arm/configs/sunxi_v8_defconfig
@@ -0,0 +1,12 @@
+CONFIG_ARCH_SUNXI=y
+CONFIG_64BIT=y
+CONFIG_BOARD_ARM_GENERIC_DT=y
+CONFIG_ARCH_SUN50I_A64=y
+CONFIG_DRIVER_SERIAL_NS16550=y
+CONFIG_MCI=y
+CONFIG_MCI_SUNXI_SMHC=y
+CONFIG_CMD_DMESG=y
+CONFIG_MCI_STARTUP=y
+CONFIG_FS_FAT=y
+CONFIG_IMAGE_COMPRESSION_XZKERN=y
+CONFIG_MMU=y
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
new file mode 100644
index 0000000000..e8d23772c2
--- /dev/null
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -0,0 +1,12 @@
+if ARCH_SUNXI
+
+config ARCH_SUN50I_A64
+	bool "Allwinner A64 SoC"
+	select CPU_V8
+	select CPU_SUPPORTS_64BIT_KERNEL
+	select CLOCKSOURCE_ARM_ARCHITECTED_TIMER
+	select PINCTRL_SUN50I_A64
+	help
+	  Allwinner A64 (sun50iw1) SoC
+
+endif
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
new file mode 100644
index 0000000000..f95691b59a
--- /dev/null
+++ b/arch/arm/mach-sunxi/Makefile
@@ -0,0 +1 @@
+obj-y += common.o
diff --git a/arch/arm/mach-sunxi/common.c b/arch/arm/mach-sunxi/common.c
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/include/mach/sunxi/barebox-arm.h b/include/mach/sunxi/barebox-arm.h
new file mode 100644
index 0000000000..2ce1db587b
--- /dev/null
+++ b/include/mach/sunxi/barebox-arm.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef MACH_SUNXI_BAREBOX_ARM_H
+#define MACH_SUNXI_BAREBOX_ARM_H
+
+#include <asm/barebox-arm.h>
+
+#endif
-- 
2.46.2




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate
  2024-11-07 14:57 ` [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate Jules Maselbas
@ 2024-11-07 15:05   ` Ahmad Fatoum
  2024-11-08 10:21   ` (subset) " Sascha Hauer
  1 sibling, 0 replies; 16+ messages in thread
From: Ahmad Fatoum @ 2024-11-07 15:05 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 07.11.24 15:57, Jules Maselbas wrote:
> In the function clk_composite_round_rate a test for a NULL function pointer
> guard the call to the round_rate callback but doesn't check for the correct
> callback: it should be round_rate instead of set_rate
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>

Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>

> ---
>  drivers/clk/clk-composite.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
> index 454bfaeb0c..fdf53ce75e 100644
> --- a/drivers/clk/clk-composite.c
> +++ b/drivers/clk/clk-composite.c
> @@ -65,7 +65,7 @@ static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
>  
>  	if (!(hw->clk.flags & CLK_SET_RATE_NO_REPARENT) &&
>  	    mux_clk &&
> -	    mux_clk->ops->set_rate)
> +	    mux_clk->ops->round_rate)
>  		return mux_clk->ops->round_rate(clk_to_clk_hw(mux_clk), rate, prate);
>  
>  	return *prate;


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (subset) [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate
  2024-11-07 14:57 ` [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate Jules Maselbas
  2024-11-07 15:05   ` Ahmad Fatoum
@ 2024-11-08 10:21   ` Sascha Hauer
  1 sibling, 0 replies; 16+ messages in thread
From: Sascha Hauer @ 2024-11-08 10:21 UTC (permalink / raw)
  To: barebox, Jules Maselbas


On Thu, 07 Nov 2024 15:57:17 +0100, Jules Maselbas wrote:
> In the function clk_composite_round_rate a test for a NULL function pointer
> guard the call to the round_rate callback but doesn't check for the correct
> callback: it should be round_rate instead of set_rate
> 
> 

Applied, thanks!

[1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate
      https://git.pengutronix.de/cgit/barebox/commit/?id=351a306c0d71 (link may not be stable)

Best regards,
-- 
Sascha Hauer <s.hauer@pengutronix.de>




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/6] clk: Add clock driver for sun50i-a64
  2024-11-07 14:57 ` [PATCH 3/6] clk: Add clock driver for sun50i-a64 Jules Maselbas
@ 2024-11-08 10:39   ` Sascha Hauer
  2024-11-08 11:08     ` Jules Maselbas
  0 siblings, 1 reply; 16+ messages in thread
From: Sascha Hauer @ 2024-11-08 10:39 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

Hi Jules,

On Thu, Nov 07, 2024 at 03:57:19PM +0100, Jules Maselbas wrote:
> Clock driver adapted from Linux.
> The pll-cpux is set to 816MHz and pll-periph0-2x is set to 1.2GHz.

>From which driver did you adapt this from? I tried copying
drivers/clk/sunxi-ng/ccu-sun50i-a64.c from Linux over your driver and it
looks quite different.

Would it be possible to get closer to the Linux driver? This would pay
off once we want to do an update from Linux.

Sascha

> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
>  drivers/clk/Makefile               |   1 +
>  drivers/clk/sunxi/Makefile         |   2 +
>  drivers/clk/sunxi/clk-sun50i-a64.c | 325 +++++++++++++++++++++++++++++
>  drivers/clk/sunxi/clk-sun50i-a64.h |  62 ++++++
>  4 files changed, 390 insertions(+)
>  create mode 100644 drivers/clk/sunxi/Makefile
>  create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
>  create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 764539e91e..7d459c298e 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -31,3 +31,4 @@ obj-y				+= bcm/
>  obj-$(CONFIG_COMMON_CLK_SCMI)	+= clk-scmi.o
>  obj-$(CONFIG_COMMON_CLK_GPIO)   += clk-gpio.o
>  obj-$(CONFIG_TI_SCI_CLK)	+= ti-sci-clk.o
> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> new file mode 100644
> index 0000000000..4d1dcbebb0
> --- /dev/null
> +++ b/drivers/clk/sunxi/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_ARCH_SUN50I_A64) += clk-sun50i-a64.o
> diff --git a/drivers/clk/sunxi/clk-sun50i-a64.c b/drivers/clk/sunxi/clk-sun50i-a64.c
> new file mode 100644
> index 0000000000..c77f366c6e
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-sun50i-a64.c
> @@ -0,0 +1,325 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// SPDX-FileCopyrightText: 2022 Jules Maselbas
> +
> +#include <common.h>
> +#include <init.h>
> +#include <driver.h>
> +#include <linux/clk.h>
> +#include <io.h>
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +
> +#define MHZ (1000UL * 1000UL)
> +
> +#include "clk-sun50i-a64.h"
> +
> +#define CCU_PLL_CPUX		0x00
> +#define CCU_PLL_PERIPH0		0x28
> +#define CCU_CPUX_AXI_CFG	0x50
> +
> +static struct clk *clks[CLK_NUMBER];
> +static struct clk_onecell_data clk_data = {
> +	.clks = clks,
> +	.clk_num = ARRAY_SIZE(clks),
> +};
> +
> +static inline struct clk *
> +sunxi_clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift)
> +{
> +	return clk_gate(name, parent, reg, shift, 0, 0);
> +}
> +
> +static inline struct clk *
> +sunxi_clk_mux(const char *name, const char * const *parents, u8 num_parents,
> +	      void __iomem *reg, u8 shift, u8 width)
> +{
> +	return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
> +}
> +
> +static inline struct clk *
> +sunxi_clk_div_table(const char *name, const char *parent, struct clk_div_table *table,
> +	      void __iomem *reg, u8 shift, u8 width)
> +{
> +	return clk_divider_table(name, parent, CLK_SET_RATE_PARENT, reg, shift,
> +				 width, table, 0);
> +}
> +
> +static inline struct clk *
> +sunxi_clk_div(const char *name, const char *parent,
> +		void __iomem *reg, u8 shift, u8 width)
> +{
> +	return clk_divider(name, parent, CLK_SET_RATE_PARENT, reg, shift,
> +				 width, 0);
> +}
> +
> +static struct clk_div_table div_apb1[] = {
> +	{ .val = 0, .div = 2 },
> +	{ .val = 1, .div = 2 },
> +	{ .val = 2, .div = 4 },
> +	{ .val = 3, .div = 8 },
> +	{ /* Sentinel */ },
> +};
> +
> +static struct clk_div_table div_N[] = {
> +	{ .val = 0, .div = 1 },
> +	{ .val = 1, .div = 2 },
> +	{ .val = 2, .div = 4 },
> +	{ .val = 3, .div = 8 },
> +	{ /* Sentinel */ },
> +};
> +
> +static const char *sel_cpux[] = {
> +	"osc32k",
> +	"osc24M",
> +	"pll-cpux",
> +};
> +
> +static const char *sel_ahb1[] = {
> +	"osc32k",
> +	"osc24M",
> +	"axi",
> +	"pll-periph0",
> +};
> +
> +static const char *sel_apb2[] = {
> +	"osc32k",
> +	"osc24M",
> +	"pll-periph0-2x",
> +	"pll-periph0-2x",
> +};
> +
> +static const char *sel_ahb2[] = {
> +	"ahb1",
> +	"pll-periph0",
> +};
> +
> +static const char *sel_mmc[] = {
> +	"osc24M",
> +	"pll-periph0-2x",
> +	"pll-periph1-2x",
> +};
> +
> +static void sun50i_a64_resets_init(void __iomem *regs)
> +{
> +	u32 rst;
> +
> +	rst = 0 |
> +		/* RST_USB_PHY0 */ BIT(0) |
> +		/* RST_USB_PHY1 */ BIT(1) |
> +		/* RST_USB_HSIC */ BIT(2);
> +	writel(rst, regs + 0x0cc);
> +
> +	rst = 0 |
> +		/* RST_BUS_MIPI_DSI */ BIT(1) |
> +		/* RST_BUS_CE   */ BIT(5) |
> +		/* RST_BUS_DMA  */ BIT(6) |
> +		/* RST_BUS_MMC0 */ BIT(8) |
> +		/* RST_BUS_MMC1 */ BIT(9) |
> +		/* RST_BUS_MMC2 */ BIT(10) |
> +		/* RST_BUS_NAND */ BIT(13) |
> +		/* RST_BUS_DRAM */ BIT(14) |
> +		/* RST_BUS_EMAC */ BIT(17) |
> +		/* RST_BUS_TS   */ BIT(18) |
> +		/* RST_BUS_HSTIMER */ BIT(19) |
> +		/* RST_BUS_SPI0  */ BIT(20) |
> +		/* RST_BUS_SPI1  */ BIT(21) |
> +		/* RST_BUS_OTG   */ BIT(23) |
> +		/* RST_BUS_EHCI0 */ BIT(24) |
> +		/* RST_BUS_EHCI1 */ BIT(25) |
> +		/* RST_BUS_OHCI0 */ BIT(28) |
> +		/* RST_BUS_OHCI1 */ BIT(29);
> +	writel(rst, regs + 0x2c0);
> +
> +	rst = 0 |
> +		/* RST_BUS_VE    */ BIT(0) |
> +		/* RST_BUS_TCON0 */ BIT(3) |
> +		/* RST_BUS_TCON1 */ BIT(4) |
> +		/* RST_BUS_DEINTERLACE */ BIT(5) |
> +		/* RST_BUS_CSI   */ BIT(8) |
> +		/* RST_BUS_HDMI0 */ BIT(10) |
> +		/* RST_BUS_HDMI1 */ BIT(11) |
> +		/* RST_BUS_DE    */ BIT(12) |
> +		/* RST_BUS_GPU   */ BIT(20) |
> +		/* RST_BUS_MSGBOX   */ BIT(21) |
> +		/* RST_BUS_SPINLOCK */ BIT(22) |
> +		/* RST_BUS_DBG */ BIT(31);
> +	writel(rst, regs + 0x2c4);
> +
> +	rst = /* RST_BUS_LVDS */ BIT(0);
> +	writel(rst, regs + 0x2c8);
> +
> +	rst = 0 |
> +		/* RST_BUS_CODEC */ BIT(0) |
> +		/* RST_BUS_SPDIF */ BIT(1) |
> +		/* RST_BUS_THS   */ BIT(8) |
> +		/* RST_BUS_I2S0  */ BIT(12) |
> +		/* RST_BUS_I2S1  */ BIT(13) |
> +		/* RST_BUS_I2S2  */ BIT(14);
> +	writel(rst, regs + 0x2d0);
> +
> +	rst = 0 |
> +		/* RST_BUS_I2C0 */ BIT(0) |
> +		/* RST_BUS_I2C1 */ BIT(1) |
> +		/* RST_BUS_I2C2 */ BIT(2) |
> +		/* RST_BUS_SCR  */ BIT(5) |
> +		/* RST_BUS_UART0 */ BIT(16) |
> +		/* RST_BUS_UART1 */ BIT(17) |
> +		/* RST_BUS_UART2 */ BIT(18) |
> +		/* RST_BUS_UART3 */ BIT(19) |
> +		/* RST_BUS_UART4 */ BIT(20);
> +	writel(rst, regs + 0x2d8);
> +}
> +
> +static void nop_delay(u32 cnt)
> +{
> +	while (cnt--)
> +		barrier();
> +}
> +
> +static inline void sunxi_clk_set_pll(void __iomem *reg, u32 src, u32 freq)
> +{
> +	/* NOTE: using u32, max freq is 4GHz
> +	 * out freq: src * N * K
> +	 * factor N: [1->32]
> +	 * factor K: [1->4]
> +	 * from the manual: give priority to the choice of K >= 2
> +	 */
> +	u32 mul = freq / src; /* target multiplier (N * K) */
> +	u32 k, n;
> +	u32 cfg = BIT(31); /* ENABLE */
> +
> +	for (k = 4; k > 1; k--) {
> +		if ((mul % k) == 0)
> +			break;
> +	}
> +	n = mul / k;
> +
> +	cfg |= (k - 1) << 4;
> +	cfg |= (n - 1) << 8;
> +
> +	writel(cfg, reg);
> +
> +	/* wait for pll lock */
> +	while (!(readl(reg) & BIT(28)));
> +}
> +
> +static void sun50i_a64_clocks_init(struct device_node *np, void __iomem *regs)
> +{
> +	sun50i_a64_resets_init(regs);
> +
> +	/* switch cpu clock source to osc-24M */
> +	writel(0x10000, regs + CCU_CPUX_AXI_CFG);
> +	/* wait 8 cycles */
> +	nop_delay(8);
> +	/* set pll-cpux to 816MHz */
> +	sunxi_clk_set_pll(regs + CCU_PLL_CPUX, 24 * MHZ, 816 * MHZ);
> +	nop_delay(10000); /* HACK: ~1ms delay  */
> +	/* switch cpu clock source to pll-cpux*/
> +	writel( /* clk_src: 1=24Mhz 2=pll-cpux */ (2 << 16) |
> +		/* apb_div: /2 */ (1 << 8) |
> +		/* axi_div: /2 */ (1 << 0),
> +		regs + CCU_CPUX_AXI_CFG);
> +	/* wait 8 cycles */
> +	nop_delay(8);
> +
> +	clks[CLK_PLL_CPUX] = clk_fixed("pll-cpux", 816 * MHZ);
> +	clks[CLK_CPUX] = sunxi_clk_mux("cpux", sel_cpux, ARRAY_SIZE(sel_cpux), regs + CCU_CPUX_AXI_CFG, 16, 2);
> +
> +	/* set pll-periph0-2x to 1.2GHz, as recommended */
> +	sunxi_clk_set_pll(regs + CCU_PLL_PERIPH0, 24 * MHZ, 1200 * MHZ);
> +
> +	clks[CLK_PLL_PERIPH0_2X] = clk_fixed("pll-periph0-2x", 1200 * MHZ);
> +	clks[CLK_PLL_PERIPH0]    = clk_fixed_factor("pll-periph0", "pll-periph0-2x", 1, 2, 0);
> +
> +	clks[CLK_AXI] = sunxi_clk_div("axi", "cpux", regs + CCU_CPUX_AXI_CFG, 0, 2);
> +
> +	clks[CLK_AHB1] = sunxi_clk_mux("ahb1", sel_ahb1, ARRAY_SIZE(sel_ahb1), regs + 0x054, 12, 2);
> +	clks[CLK_AHB2] = sunxi_clk_mux("ahb2", sel_ahb2, ARRAY_SIZE(sel_ahb2), regs + 0x05c, 0, 1);
> +
> +	clks[CLK_APB1] = sunxi_clk_div_table("apb1", "ahb1", div_apb1, regs + 0x054, 8, 2);
> +	clks[CLK_APB2] = sunxi_clk_mux("apb2", sel_apb2, ARRAY_SIZE(sel_apb2), regs + 0x058, 24, 2);
> +
> +	clks[CLK_BUS_MIPI_DSI] = sunxi_clk_gate("bus-mipi-dsi","ahb1",regs + 0x060, 1);
> +	clks[CLK_BUS_CE]    = sunxi_clk_gate("bus-ce",    "ahb1", regs + 0x060, 5);
> +	clks[CLK_BUS_DMA]   = sunxi_clk_gate("bus-dma",   "ahb1", regs + 0x060, 6);
> +	clks[CLK_BUS_MMC0]  = sunxi_clk_gate("bus-mmc0",  "ahb1", regs + 0x060, 8);
> +	clks[CLK_BUS_MMC1]  = sunxi_clk_gate("bus-mmc1",  "ahb1", regs + 0x060, 9);
> +	clks[CLK_BUS_MMC2]  = sunxi_clk_gate("bus-mmc2",  "ahb1", regs + 0x060, 10);
> +	clks[CLK_BUS_NAND]  = sunxi_clk_gate("bus-nand",  "ahb1", regs + 0x060, 13);
> +	clks[CLK_BUS_DRAM]  = sunxi_clk_gate("bus-dram",  "ahb1", regs + 0x060, 14);
> +	clks[CLK_BUS_EMAC]  = sunxi_clk_gate("bus-emac",  "ahb2", regs + 0x060, 17);
> +	clks[CLK_BUS_TS]    = sunxi_clk_gate("bus-ts",    "ahb1", regs + 0x060, 18);
> +	clks[CLK_BUS_HSTIMER] = sunxi_clk_gate("bus-hstimer", "ahb1", regs + 0x060, 19);
> +	clks[CLK_BUS_SPI0]  = sunxi_clk_gate("bus-spi0",  "ahb1", regs + 0x060,  20);
> +	clks[CLK_BUS_SPI1]  = sunxi_clk_gate("bus-spi1",  "ahb1", regs + 0x060,  21);
> +	clks[CLK_BUS_OTG]   = sunxi_clk_gate("bus-otg",   "ahb1", regs + 0x060,  23);
> +	clks[CLK_BUS_EHCI0] = sunxi_clk_gate("bus-ehci0", "ahb1", regs + 0x060, 24);
> +	clks[CLK_BUS_EHCI1] = sunxi_clk_gate("bus-ehci1", "ahb2", regs + 0x060, 25);
> +	clks[CLK_BUS_OHCI0] = sunxi_clk_gate("bus-ohci0", "ahb1", regs + 0x060, 28);
> +	clks[CLK_BUS_OHCI1] = sunxi_clk_gate("bus-ohci1", "ahb2", regs + 0x060, 29);
> +
> +	clks[CLK_BUS_CODEC] = sunxi_clk_gate("bus-codec", "apb1", regs + 0x068, 0);
> +	clks[CLK_BUS_SPDIF] = sunxi_clk_gate("bus-spdif", "apb1", regs + 0x068, 1);
> +	clks[CLK_BUS_PIO]   = sunxi_clk_gate("bus-pio",   "apb1", regs + 0x068, 5);
> +	clks[CLK_BUS_THS]   = sunxi_clk_gate("bus-ths",   "apb1", regs + 0x068, 8);
> +	clks[CLK_BUS_I2S0]  = sunxi_clk_gate("bus-i2s0",  "apb1", regs + 0x068, 12);
> +	clks[CLK_BUS_I2S1]  = sunxi_clk_gate("bus-i2s1",  "apb1", regs + 0x068, 13);
> +	clks[CLK_BUS_I2S2]  = sunxi_clk_gate("bus-i2s2",  "apb1", regs + 0x068, 14);
> +
> +	clks[CLK_BUS_UART0] = sunxi_clk_gate("bus-uart0", "apb2", regs + 0x06c, 16);
> +	clks[CLK_BUS_UART1] = sunxi_clk_gate("bus-uart1", "apb2", regs + 0x06c, 17);
> +	clks[CLK_BUS_UART2] = sunxi_clk_gate("bus-uart2", "apb2", regs + 0x06c, 18);
> +	clks[CLK_BUS_UART3] = sunxi_clk_gate("bus-uart3", "apb2", regs + 0x06c, 19);
> +	clks[CLK_BUS_UART4] = sunxi_clk_gate("bus-uart4", "apb2", regs + 0x06c, 20);
> +
> +	writel(0, regs + 0x088);
> +	clks[CLK_MMC0] = clk_register_composite(
> +		"mmc0",	sel_mmc, ARRAY_SIZE(sel_mmc),
> +		sunxi_clk_mux("mmc0-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x088, 24, 2),
> +		sunxi_clk_div("mmc0-div-m", "mmc0-mux", regs + 0x088, 0, 4),
> +		sunxi_clk_gate("mmc0-gate", "mmc0-div-n", regs + 0x088, 31),
> +		0);
> +
> +	writel(0, regs + 0x08c);
> +	clks[CLK_MMC1] = clk_register_composite(
> +		"mmc1",	sel_mmc, ARRAY_SIZE(sel_mmc),
> +		sunxi_clk_mux("mmc1-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x08c, 24, 2),
> +		sunxi_clk_div("mmc1-div-m", "mmc1-mux", regs + 0x08c, 0, 4),
> +		sunxi_clk_gate("mmc1-gate", "mmc1-div-n", regs + 0x08c, 31),
> +		0);
> +
> +	writel(0, regs + 0x090);
> +	clks[CLK_MMC2] = clk_register_composite(
> +		"mmc2",	sel_mmc, ARRAY_SIZE(sel_mmc),
> +		sunxi_clk_mux("mmc2-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x090, 24, 2),
> +		sunxi_clk_div("mmc2-div-m", "mmc2-mux", regs + 0x090, 0, 4),
> +		sunxi_clk_gate("mmc2-gate", "mmc2-div-n", regs + 0x090, 31),
> +		0);
> +}
> +
> +static int sun50i_a64_ccu_probe(struct device *dev)
> +{
> +	struct resource *iores;
> +
> +	iores = dev_request_mem_resource(dev, 0);
> +	if (IS_ERR(iores))
> +		return PTR_ERR(iores);
> +
> +	sun50i_a64_clocks_init(dev->of_node, IOMEM(iores->start));
> +	return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
> +}
> +
> +static __maybe_unused struct of_device_id sun50i_a64_ccu_dt_ids[] = {
> +	{
> +		.compatible = "allwinner,sun50i-a64-ccu",
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +
> +static struct driver sun50i_a64_ccu_driver = {
> +	.probe	= sun50i_a64_ccu_probe,
> +	.name	= "sun50i-a64-ccu",
> +	.of_compatible = DRV_OF_COMPAT(sun50i_a64_ccu_dt_ids),
> +};
> +core_platform_driver(sun50i_a64_ccu_driver);
> diff --git a/drivers/clk/sunxi/clk-sun50i-a64.h b/drivers/clk/sunxi/clk-sun50i-a64.h
> new file mode 100644
> index 0000000000..a4ddc39eb8
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-sun50i-a64.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright 2016 Maxime Ripard
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + */
> +
> +#include <dt-bindings/clock/sun50i-a64-ccu.h>
> +
> +#ifndef _CLK_SUN50I_A64_H_
> +#define _CLK_SUN50I_A64_H_
> +
> +#include <dt-bindings/clock/sun50i-a64-ccu.h>
> +#include <dt-bindings/reset/sun50i-a64-ccu.h>
> +
> +#define CLK_OSC_12M			0
> +#define CLK_PLL_CPUX			1
> +#define CLK_PLL_AUDIO_BASE		2
> +#define CLK_PLL_AUDIO			3
> +#define CLK_PLL_AUDIO_2X		4
> +#define CLK_PLL_AUDIO_4X		5
> +#define CLK_PLL_AUDIO_8X		6
> +
> +/* PLL_VIDEO0 exported for HDMI PHY */
> +
> +#define CLK_PLL_VIDEO0_2X		8
> +#define CLK_PLL_VE			9
> +#define CLK_PLL_DDR0			10
> +
> +/* PLL_PERIPH0 exported for PRCM */
> +
> +#define CLK_PLL_PERIPH0_2X		12
> +#define CLK_PLL_PERIPH1			13
> +#define CLK_PLL_PERIPH1_2X		14
> +#define CLK_PLL_VIDEO1			15
> +#define CLK_PLL_GPU			16
> +#define CLK_PLL_MIPI			17
> +#define CLK_PLL_HSIC			18
> +#define CLK_PLL_DE			19
> +#define CLK_PLL_DDR1			20
> +#define CLK_AXI				22
> +#define CLK_APB				23
> +#define CLK_AHB1			24
> +#define CLK_APB1			25
> +#define CLK_APB2			26
> +#define CLK_AHB2			27
> +
> +/* All the bus gates are exported */
> +
> +/* The first bunch of module clocks are exported */
> +
> +#define CLK_USB_OHCI0_12M		90
> +
> +#define CLK_USB_OHCI1_12M		92
> +
> +/* All the DRAM gates are exported */
> +
> +/* And the DSI and GPU module clock is exported */
> +
> +#define CLK_NUMBER			(CLK_GPU + 1)
> +
> +#endif /* _CLK_SUN50I_A64_H_ */
> -- 
> 2.46.2
> 
> 
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/6] pinctrl: Add sun50i-a64 pinctrl driver
  2024-11-07 14:57 ` [PATCH 4/6] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
@ 2024-11-08 10:48   ` Sascha Hauer
  0 siblings, 0 replies; 16+ messages in thread
From: Sascha Hauer @ 2024-11-08 10:48 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, Nov 07, 2024 at 03:57:20PM +0100, Jules Maselbas wrote:
> sunxi pinctrl driver, adapted from Linux, is split in two parts:
>  - pinctrl-sunxi.c that implements gpio, pinctrl and pinmux functions
>  - pinctrl-sun50i-a64.c that declare sun50i pins and their functions.
>    This file only require minor adaptations and be easily copied from Linux.
> 
> The pin functions are needed for uart, SD/MMC, and likely for other
> controllers too.
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
>  drivers/pinctrl/Kconfig                    |   2 +
>  drivers/pinctrl/Makefile                   |   1 +
>  drivers/pinctrl/sunxi/Kconfig              |  13 +
>  drivers/pinctrl/sunxi/Makefile             |   3 +
>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 594 +++++++++++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 372 +++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
>  7 files changed, 1209 insertions(+)
>  create mode 100644 drivers/pinctrl/sunxi/Kconfig
>  create mode 100644 drivers/pinctrl/sunxi/Makefile
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
> 

...

> +
> +int sunxi_pinctrl_probe(struct device *dev)
> +{
> +	const struct sunxi_pinctrl_desc *desc;
> +	struct sunxi_pinctrl *pinctrl;
> +	struct resource *iores;
> +	int ret;
> +
> +	if (!IS_ENABLED(CONFIG_PINCTRL))
> +		return 0;

You can drop this. A few drivers have it because they provide GPIO
support even with pinctrl support disabled, but you don't do this here.

Sascha

> +
> +	desc = device_get_match_data(dev);
> +	if (!desc)
> +                return -EINVAL;
> +


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/6] clk: Add clock driver for sun50i-a64
  2024-11-08 10:39   ` Sascha Hauer
@ 2024-11-08 11:08     ` Jules Maselbas
  2024-11-08 12:59       ` Sascha Hauer
  0 siblings, 1 reply; 16+ messages in thread
From: Jules Maselbas @ 2024-11-08 11:08 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

Hi Sascha,

On Fri Nov 8, 2024 at 11:39 AM CET, Sascha Hauer wrote:
> Hi Jules,
>
> On Thu, Nov 07, 2024 at 03:57:19PM +0100, Jules Maselbas wrote:
> > Clock driver adapted from Linux.
> > The pll-cpux is set to 816MHz and pll-periph0-2x is set to 1.2GHz.
>
> From which driver did you adapt this from? I tried copying
> drivers/clk/sunxi-ng/ccu-sun50i-a64.c from Linux over your driver and it
> looks quite different.
Yes this is adapted from drivers/clk/sunxi-ng/ccu-sun50i-a64.c, but it is very
different. This is a very stripped down version. I wanted to avoid having to
porting all the clock/gate/div code for sunxi and use as much as possible the
one available in barebox. 

> Would it be possible to get closer to the Linux driver? This would pay
> off once we want to do an update from Linux.
Humm...
I could try to get it closer, i would like to not having to copy all the ccu*
drivers, but maybe i could define barebox version of SUNXI_CCU_* macros

Jules

>
> Sascha
>
> > 
> > Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> > ---
> >  drivers/clk/Makefile               |   1 +
> >  drivers/clk/sunxi/Makefile         |   2 +
> >  drivers/clk/sunxi/clk-sun50i-a64.c | 325 +++++++++++++++++++++++++++++
> >  drivers/clk/sunxi/clk-sun50i-a64.h |  62 ++++++
> >  4 files changed, 390 insertions(+)
> >  create mode 100644 drivers/clk/sunxi/Makefile
> >  create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
> >  create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h
> > 
> > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> > index 764539e91e..7d459c298e 100644
> > --- a/drivers/clk/Makefile
> > +++ b/drivers/clk/Makefile
> > @@ -31,3 +31,4 @@ obj-y				+= bcm/
> >  obj-$(CONFIG_COMMON_CLK_SCMI)	+= clk-scmi.o
> >  obj-$(CONFIG_COMMON_CLK_GPIO)   += clk-gpio.o
> >  obj-$(CONFIG_TI_SCI_CLK)	+= ti-sci-clk.o
> > +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
> > diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> > new file mode 100644
> > index 0000000000..4d1dcbebb0
> > --- /dev/null
> > +++ b/drivers/clk/sunxi/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +obj-$(CONFIG_ARCH_SUN50I_A64) += clk-sun50i-a64.o
> > diff --git a/drivers/clk/sunxi/clk-sun50i-a64.c b/drivers/clk/sunxi/clk-sun50i-a64.c
> > new file mode 100644
> > index 0000000000..c77f366c6e
> > --- /dev/null
> > +++ b/drivers/clk/sunxi/clk-sun50i-a64.c
> > @@ -0,0 +1,325 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +// SPDX-FileCopyrightText: 2022 Jules Maselbas
> > +
> > +#include <common.h>
> > +#include <init.h>
> > +#include <driver.h>
> > +#include <linux/clk.h>
> > +#include <io.h>
> > +#include <linux/clkdev.h>
> > +#include <linux/err.h>
> > +
> > +#define MHZ (1000UL * 1000UL)
> > +
> > +#include "clk-sun50i-a64.h"
> > +
> > +#define CCU_PLL_CPUX		0x00
> > +#define CCU_PLL_PERIPH0		0x28
> > +#define CCU_CPUX_AXI_CFG	0x50
> > +
> > +static struct clk *clks[CLK_NUMBER];
> > +static struct clk_onecell_data clk_data = {
> > +	.clks = clks,
> > +	.clk_num = ARRAY_SIZE(clks),
> > +};
> > +
> > +static inline struct clk *
> > +sunxi_clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift)
> > +{
> > +	return clk_gate(name, parent, reg, shift, 0, 0);
> > +}
> > +
> > +static inline struct clk *
> > +sunxi_clk_mux(const char *name, const char * const *parents, u8 num_parents,
> > +	      void __iomem *reg, u8 shift, u8 width)
> > +{
> > +	return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
> > +}
> > +
> > +static inline struct clk *
> > +sunxi_clk_div_table(const char *name, const char *parent, struct clk_div_table *table,
> > +	      void __iomem *reg, u8 shift, u8 width)
> > +{
> > +	return clk_divider_table(name, parent, CLK_SET_RATE_PARENT, reg, shift,
> > +				 width, table, 0);
> > +}
> > +
> > +static inline struct clk *
> > +sunxi_clk_div(const char *name, const char *parent,
> > +		void __iomem *reg, u8 shift, u8 width)
> > +{
> > +	return clk_divider(name, parent, CLK_SET_RATE_PARENT, reg, shift,
> > +				 width, 0);
> > +}
> > +
> > +static struct clk_div_table div_apb1[] = {
> > +	{ .val = 0, .div = 2 },
> > +	{ .val = 1, .div = 2 },
> > +	{ .val = 2, .div = 4 },
> > +	{ .val = 3, .div = 8 },
> > +	{ /* Sentinel */ },
> > +};
> > +
> > +static struct clk_div_table div_N[] = {
> > +	{ .val = 0, .div = 1 },
> > +	{ .val = 1, .div = 2 },
> > +	{ .val = 2, .div = 4 },
> > +	{ .val = 3, .div = 8 },
> > +	{ /* Sentinel */ },
> > +};
> > +
> > +static const char *sel_cpux[] = {
> > +	"osc32k",
> > +	"osc24M",
> > +	"pll-cpux",
> > +};
> > +
> > +static const char *sel_ahb1[] = {
> > +	"osc32k",
> > +	"osc24M",
> > +	"axi",
> > +	"pll-periph0",
> > +};
> > +
> > +static const char *sel_apb2[] = {
> > +	"osc32k",
> > +	"osc24M",
> > +	"pll-periph0-2x",
> > +	"pll-periph0-2x",
> > +};
> > +
> > +static const char *sel_ahb2[] = {
> > +	"ahb1",
> > +	"pll-periph0",
> > +};
> > +
> > +static const char *sel_mmc[] = {
> > +	"osc24M",
> > +	"pll-periph0-2x",
> > +	"pll-periph1-2x",
> > +};
> > +
> > +static void sun50i_a64_resets_init(void __iomem *regs)
> > +{
> > +	u32 rst;
> > +
> > +	rst = 0 |
> > +		/* RST_USB_PHY0 */ BIT(0) |
> > +		/* RST_USB_PHY1 */ BIT(1) |
> > +		/* RST_USB_HSIC */ BIT(2);
> > +	writel(rst, regs + 0x0cc);
> > +
> > +	rst = 0 |
> > +		/* RST_BUS_MIPI_DSI */ BIT(1) |
> > +		/* RST_BUS_CE   */ BIT(5) |
> > +		/* RST_BUS_DMA  */ BIT(6) |
> > +		/* RST_BUS_MMC0 */ BIT(8) |
> > +		/* RST_BUS_MMC1 */ BIT(9) |
> > +		/* RST_BUS_MMC2 */ BIT(10) |
> > +		/* RST_BUS_NAND */ BIT(13) |
> > +		/* RST_BUS_DRAM */ BIT(14) |
> > +		/* RST_BUS_EMAC */ BIT(17) |
> > +		/* RST_BUS_TS   */ BIT(18) |
> > +		/* RST_BUS_HSTIMER */ BIT(19) |
> > +		/* RST_BUS_SPI0  */ BIT(20) |
> > +		/* RST_BUS_SPI1  */ BIT(21) |
> > +		/* RST_BUS_OTG   */ BIT(23) |
> > +		/* RST_BUS_EHCI0 */ BIT(24) |
> > +		/* RST_BUS_EHCI1 */ BIT(25) |
> > +		/* RST_BUS_OHCI0 */ BIT(28) |
> > +		/* RST_BUS_OHCI1 */ BIT(29);
> > +	writel(rst, regs + 0x2c0);
> > +
> > +	rst = 0 |
> > +		/* RST_BUS_VE    */ BIT(0) |
> > +		/* RST_BUS_TCON0 */ BIT(3) |
> > +		/* RST_BUS_TCON1 */ BIT(4) |
> > +		/* RST_BUS_DEINTERLACE */ BIT(5) |
> > +		/* RST_BUS_CSI   */ BIT(8) |
> > +		/* RST_BUS_HDMI0 */ BIT(10) |
> > +		/* RST_BUS_HDMI1 */ BIT(11) |
> > +		/* RST_BUS_DE    */ BIT(12) |
> > +		/* RST_BUS_GPU   */ BIT(20) |
> > +		/* RST_BUS_MSGBOX   */ BIT(21) |
> > +		/* RST_BUS_SPINLOCK */ BIT(22) |
> > +		/* RST_BUS_DBG */ BIT(31);
> > +	writel(rst, regs + 0x2c4);
> > +
> > +	rst = /* RST_BUS_LVDS */ BIT(0);
> > +	writel(rst, regs + 0x2c8);
> > +
> > +	rst = 0 |
> > +		/* RST_BUS_CODEC */ BIT(0) |
> > +		/* RST_BUS_SPDIF */ BIT(1) |
> > +		/* RST_BUS_THS   */ BIT(8) |
> > +		/* RST_BUS_I2S0  */ BIT(12) |
> > +		/* RST_BUS_I2S1  */ BIT(13) |
> > +		/* RST_BUS_I2S2  */ BIT(14);
> > +	writel(rst, regs + 0x2d0);
> > +
> > +	rst = 0 |
> > +		/* RST_BUS_I2C0 */ BIT(0) |
> > +		/* RST_BUS_I2C1 */ BIT(1) |
> > +		/* RST_BUS_I2C2 */ BIT(2) |
> > +		/* RST_BUS_SCR  */ BIT(5) |
> > +		/* RST_BUS_UART0 */ BIT(16) |
> > +		/* RST_BUS_UART1 */ BIT(17) |
> > +		/* RST_BUS_UART2 */ BIT(18) |
> > +		/* RST_BUS_UART3 */ BIT(19) |
> > +		/* RST_BUS_UART4 */ BIT(20);
> > +	writel(rst, regs + 0x2d8);
> > +}
> > +
> > +static void nop_delay(u32 cnt)
> > +{
> > +	while (cnt--)
> > +		barrier();
> > +}
> > +
> > +static inline void sunxi_clk_set_pll(void __iomem *reg, u32 src, u32 freq)
> > +{
> > +	/* NOTE: using u32, max freq is 4GHz
> > +	 * out freq: src * N * K
> > +	 * factor N: [1->32]
> > +	 * factor K: [1->4]
> > +	 * from the manual: give priority to the choice of K >= 2
> > +	 */
> > +	u32 mul = freq / src; /* target multiplier (N * K) */
> > +	u32 k, n;
> > +	u32 cfg = BIT(31); /* ENABLE */
> > +
> > +	for (k = 4; k > 1; k--) {
> > +		if ((mul % k) == 0)
> > +			break;
> > +	}
> > +	n = mul / k;
> > +
> > +	cfg |= (k - 1) << 4;
> > +	cfg |= (n - 1) << 8;
> > +
> > +	writel(cfg, reg);
> > +
> > +	/* wait for pll lock */
> > +	while (!(readl(reg) & BIT(28)));
> > +}
> > +
> > +static void sun50i_a64_clocks_init(struct device_node *np, void __iomem *regs)
> > +{
> > +	sun50i_a64_resets_init(regs);
> > +
> > +	/* switch cpu clock source to osc-24M */
> > +	writel(0x10000, regs + CCU_CPUX_AXI_CFG);
> > +	/* wait 8 cycles */
> > +	nop_delay(8);
> > +	/* set pll-cpux to 816MHz */
> > +	sunxi_clk_set_pll(regs + CCU_PLL_CPUX, 24 * MHZ, 816 * MHZ);
> > +	nop_delay(10000); /* HACK: ~1ms delay  */
> > +	/* switch cpu clock source to pll-cpux*/
> > +	writel( /* clk_src: 1=24Mhz 2=pll-cpux */ (2 << 16) |
> > +		/* apb_div: /2 */ (1 << 8) |
> > +		/* axi_div: /2 */ (1 << 0),
> > +		regs + CCU_CPUX_AXI_CFG);
> > +	/* wait 8 cycles */
> > +	nop_delay(8);
> > +
> > +	clks[CLK_PLL_CPUX] = clk_fixed("pll-cpux", 816 * MHZ);
> > +	clks[CLK_CPUX] = sunxi_clk_mux("cpux", sel_cpux, ARRAY_SIZE(sel_cpux), regs + CCU_CPUX_AXI_CFG, 16, 2);
> > +
> > +	/* set pll-periph0-2x to 1.2GHz, as recommended */
> > +	sunxi_clk_set_pll(regs + CCU_PLL_PERIPH0, 24 * MHZ, 1200 * MHZ);
> > +
> > +	clks[CLK_PLL_PERIPH0_2X] = clk_fixed("pll-periph0-2x", 1200 * MHZ);
> > +	clks[CLK_PLL_PERIPH0]    = clk_fixed_factor("pll-periph0", "pll-periph0-2x", 1, 2, 0);
> > +
> > +	clks[CLK_AXI] = sunxi_clk_div("axi", "cpux", regs + CCU_CPUX_AXI_CFG, 0, 2);
> > +
> > +	clks[CLK_AHB1] = sunxi_clk_mux("ahb1", sel_ahb1, ARRAY_SIZE(sel_ahb1), regs + 0x054, 12, 2);
> > +	clks[CLK_AHB2] = sunxi_clk_mux("ahb2", sel_ahb2, ARRAY_SIZE(sel_ahb2), regs + 0x05c, 0, 1);
> > +
> > +	clks[CLK_APB1] = sunxi_clk_div_table("apb1", "ahb1", div_apb1, regs + 0x054, 8, 2);
> > +	clks[CLK_APB2] = sunxi_clk_mux("apb2", sel_apb2, ARRAY_SIZE(sel_apb2), regs + 0x058, 24, 2);
> > +
> > +	clks[CLK_BUS_MIPI_DSI] = sunxi_clk_gate("bus-mipi-dsi","ahb1",regs + 0x060, 1);
> > +	clks[CLK_BUS_CE]    = sunxi_clk_gate("bus-ce",    "ahb1", regs + 0x060, 5);
> > +	clks[CLK_BUS_DMA]   = sunxi_clk_gate("bus-dma",   "ahb1", regs + 0x060, 6);
> > +	clks[CLK_BUS_MMC0]  = sunxi_clk_gate("bus-mmc0",  "ahb1", regs + 0x060, 8);
> > +	clks[CLK_BUS_MMC1]  = sunxi_clk_gate("bus-mmc1",  "ahb1", regs + 0x060, 9);
> > +	clks[CLK_BUS_MMC2]  = sunxi_clk_gate("bus-mmc2",  "ahb1", regs + 0x060, 10);
> > +	clks[CLK_BUS_NAND]  = sunxi_clk_gate("bus-nand",  "ahb1", regs + 0x060, 13);
> > +	clks[CLK_BUS_DRAM]  = sunxi_clk_gate("bus-dram",  "ahb1", regs + 0x060, 14);
> > +	clks[CLK_BUS_EMAC]  = sunxi_clk_gate("bus-emac",  "ahb2", regs + 0x060, 17);
> > +	clks[CLK_BUS_TS]    = sunxi_clk_gate("bus-ts",    "ahb1", regs + 0x060, 18);
> > +	clks[CLK_BUS_HSTIMER] = sunxi_clk_gate("bus-hstimer", "ahb1", regs + 0x060, 19);
> > +	clks[CLK_BUS_SPI0]  = sunxi_clk_gate("bus-spi0",  "ahb1", regs + 0x060,  20);
> > +	clks[CLK_BUS_SPI1]  = sunxi_clk_gate("bus-spi1",  "ahb1", regs + 0x060,  21);
> > +	clks[CLK_BUS_OTG]   = sunxi_clk_gate("bus-otg",   "ahb1", regs + 0x060,  23);
> > +	clks[CLK_BUS_EHCI0] = sunxi_clk_gate("bus-ehci0", "ahb1", regs + 0x060, 24);
> > +	clks[CLK_BUS_EHCI1] = sunxi_clk_gate("bus-ehci1", "ahb2", regs + 0x060, 25);
> > +	clks[CLK_BUS_OHCI0] = sunxi_clk_gate("bus-ohci0", "ahb1", regs + 0x060, 28);
> > +	clks[CLK_BUS_OHCI1] = sunxi_clk_gate("bus-ohci1", "ahb2", regs + 0x060, 29);
> > +
> > +	clks[CLK_BUS_CODEC] = sunxi_clk_gate("bus-codec", "apb1", regs + 0x068, 0);
> > +	clks[CLK_BUS_SPDIF] = sunxi_clk_gate("bus-spdif", "apb1", regs + 0x068, 1);
> > +	clks[CLK_BUS_PIO]   = sunxi_clk_gate("bus-pio",   "apb1", regs + 0x068, 5);
> > +	clks[CLK_BUS_THS]   = sunxi_clk_gate("bus-ths",   "apb1", regs + 0x068, 8);
> > +	clks[CLK_BUS_I2S0]  = sunxi_clk_gate("bus-i2s0",  "apb1", regs + 0x068, 12);
> > +	clks[CLK_BUS_I2S1]  = sunxi_clk_gate("bus-i2s1",  "apb1", regs + 0x068, 13);
> > +	clks[CLK_BUS_I2S2]  = sunxi_clk_gate("bus-i2s2",  "apb1", regs + 0x068, 14);
> > +
> > +	clks[CLK_BUS_UART0] = sunxi_clk_gate("bus-uart0", "apb2", regs + 0x06c, 16);
> > +	clks[CLK_BUS_UART1] = sunxi_clk_gate("bus-uart1", "apb2", regs + 0x06c, 17);
> > +	clks[CLK_BUS_UART2] = sunxi_clk_gate("bus-uart2", "apb2", regs + 0x06c, 18);
> > +	clks[CLK_BUS_UART3] = sunxi_clk_gate("bus-uart3", "apb2", regs + 0x06c, 19);
> > +	clks[CLK_BUS_UART4] = sunxi_clk_gate("bus-uart4", "apb2", regs + 0x06c, 20);
> > +
> > +	writel(0, regs + 0x088);
> > +	clks[CLK_MMC0] = clk_register_composite(
> > +		"mmc0",	sel_mmc, ARRAY_SIZE(sel_mmc),
> > +		sunxi_clk_mux("mmc0-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x088, 24, 2),
> > +		sunxi_clk_div("mmc0-div-m", "mmc0-mux", regs + 0x088, 0, 4),
> > +		sunxi_clk_gate("mmc0-gate", "mmc0-div-n", regs + 0x088, 31),
> > +		0);
> > +
> > +	writel(0, regs + 0x08c);
> > +	clks[CLK_MMC1] = clk_register_composite(
> > +		"mmc1",	sel_mmc, ARRAY_SIZE(sel_mmc),
> > +		sunxi_clk_mux("mmc1-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x08c, 24, 2),
> > +		sunxi_clk_div("mmc1-div-m", "mmc1-mux", regs + 0x08c, 0, 4),
> > +		sunxi_clk_gate("mmc1-gate", "mmc1-div-n", regs + 0x08c, 31),
> > +		0);
> > +
> > +	writel(0, regs + 0x090);
> > +	clks[CLK_MMC2] = clk_register_composite(
> > +		"mmc2",	sel_mmc, ARRAY_SIZE(sel_mmc),
> > +		sunxi_clk_mux("mmc2-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x090, 24, 2),
> > +		sunxi_clk_div("mmc2-div-m", "mmc2-mux", regs + 0x090, 0, 4),
> > +		sunxi_clk_gate("mmc2-gate", "mmc2-div-n", regs + 0x090, 31),
> > +		0);
> > +}
> > +
> > +static int sun50i_a64_ccu_probe(struct device *dev)
> > +{
> > +	struct resource *iores;
> > +
> > +	iores = dev_request_mem_resource(dev, 0);
> > +	if (IS_ERR(iores))
> > +		return PTR_ERR(iores);
> > +
> > +	sun50i_a64_clocks_init(dev->of_node, IOMEM(iores->start));
> > +	return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
> > +}
> > +
> > +static __maybe_unused struct of_device_id sun50i_a64_ccu_dt_ids[] = {
> > +	{
> > +		.compatible = "allwinner,sun50i-a64-ccu",
> > +	}, {
> > +		/* sentinel */
> > +	}
> > +};
> > +
> > +static struct driver sun50i_a64_ccu_driver = {
> > +	.probe	= sun50i_a64_ccu_probe,
> > +	.name	= "sun50i-a64-ccu",
> > +	.of_compatible = DRV_OF_COMPAT(sun50i_a64_ccu_dt_ids),
> > +};
> > +core_platform_driver(sun50i_a64_ccu_driver);
> > diff --git a/drivers/clk/sunxi/clk-sun50i-a64.h b/drivers/clk/sunxi/clk-sun50i-a64.h
> > new file mode 100644
> > index 0000000000..a4ddc39eb8
> > --- /dev/null
> > +++ b/drivers/clk/sunxi/clk-sun50i-a64.h
> > @@ -0,0 +1,62 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright 2016 Maxime Ripard
> > + *
> > + * Maxime Ripard <maxime.ripard@free-electrons.com>
> > + */
> > +
> > +#include <dt-bindings/clock/sun50i-a64-ccu.h>
> > +
> > +#ifndef _CLK_SUN50I_A64_H_
> > +#define _CLK_SUN50I_A64_H_
> > +
> > +#include <dt-bindings/clock/sun50i-a64-ccu.h>
> > +#include <dt-bindings/reset/sun50i-a64-ccu.h>
> > +
> > +#define CLK_OSC_12M			0
> > +#define CLK_PLL_CPUX			1
> > +#define CLK_PLL_AUDIO_BASE		2
> > +#define CLK_PLL_AUDIO			3
> > +#define CLK_PLL_AUDIO_2X		4
> > +#define CLK_PLL_AUDIO_4X		5
> > +#define CLK_PLL_AUDIO_8X		6
> > +
> > +/* PLL_VIDEO0 exported for HDMI PHY */
> > +
> > +#define CLK_PLL_VIDEO0_2X		8
> > +#define CLK_PLL_VE			9
> > +#define CLK_PLL_DDR0			10
> > +
> > +/* PLL_PERIPH0 exported for PRCM */
> > +
> > +#define CLK_PLL_PERIPH0_2X		12
> > +#define CLK_PLL_PERIPH1			13
> > +#define CLK_PLL_PERIPH1_2X		14
> > +#define CLK_PLL_VIDEO1			15
> > +#define CLK_PLL_GPU			16
> > +#define CLK_PLL_MIPI			17
> > +#define CLK_PLL_HSIC			18
> > +#define CLK_PLL_DE			19
> > +#define CLK_PLL_DDR1			20
> > +#define CLK_AXI				22
> > +#define CLK_APB				23
> > +#define CLK_AHB1			24
> > +#define CLK_APB1			25
> > +#define CLK_APB2			26
> > +#define CLK_AHB2			27
> > +
> > +/* All the bus gates are exported */
> > +
> > +/* The first bunch of module clocks are exported */
> > +
> > +#define CLK_USB_OHCI0_12M		90
> > +
> > +#define CLK_USB_OHCI1_12M		92
> > +
> > +/* All the DRAM gates are exported */
> > +
> > +/* And the DSI and GPU module clock is exported */
> > +
> > +#define CLK_NUMBER			(CLK_GPU + 1)
> > +
> > +#endif /* _CLK_SUN50I_A64_H_ */
> > -- 
> > 2.46.2
> > 
> > 
> > 




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/6] clk: Add clock driver for sun50i-a64
  2024-11-08 11:08     ` Jules Maselbas
@ 2024-11-08 12:59       ` Sascha Hauer
  2024-11-12 15:58         ` Jules Maselbas
  0 siblings, 1 reply; 16+ messages in thread
From: Sascha Hauer @ 2024-11-08 12:59 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Fri, Nov 08, 2024 at 12:08:45PM +0100, Jules Maselbas wrote:
> Hi Sascha,
> 
> On Fri Nov 8, 2024 at 11:39 AM CET, Sascha Hauer wrote:
> > Hi Jules,
> >
> > On Thu, Nov 07, 2024 at 03:57:19PM +0100, Jules Maselbas wrote:
> > > Clock driver adapted from Linux.
> > > The pll-cpux is set to 816MHz and pll-periph0-2x is set to 1.2GHz.
> >
> > From which driver did you adapt this from? I tried copying
> > drivers/clk/sunxi-ng/ccu-sun50i-a64.c from Linux over your driver and it
> > looks quite different.
> Yes this is adapted from drivers/clk/sunxi-ng/ccu-sun50i-a64.c, but it is very
> different. This is a very stripped down version. I wanted to avoid having to
> porting all the clock/gate/div code for sunxi and use as much as possible the
> one available in barebox.
> 
> > Would it be possible to get closer to the Linux driver? This would pay
> > off once we want to do an update from Linux.
> Humm...
> I could try to get it closer, i would like to not having to copy all the ccu*
> drivers, but maybe i could define barebox version of SUNXI_CCU_* macros

In my experience Linux clk drivers fit quite nicely into barebox when
just blindly throwing the code into it. I haven't got any experience
with the allwinner clk code though, so your mileage may vary.

Putting the (mostly) unmodified versions into barebox has the advantage
that updating from Linux is easy and adding new SoC support is easy as
well.

That said, it's fine to have heavily modified ckl driver versions in
barebox and we can try that if you like. We can always revisit this
decision should we have to.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/6] clk: Add clock driver for sun50i-a64
  2024-11-08 12:59       ` Sascha Hauer
@ 2024-11-12 15:58         ` Jules Maselbas
  2024-11-13  8:42           ` Sascha Hauer
  0 siblings, 1 reply; 16+ messages in thread
From: Jules Maselbas @ 2024-11-12 15:58 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

resent, as i forgot barebox ml in CC

On Fri Nov 8, 2024 at 1:59 PM CET, Sascha Hauer wrote:
> On Fri, Nov 08, 2024 at 12:08:45PM +0100, Jules Maselbas wrote:
> > Hi Sascha,
> >
> > On Fri Nov 8, 2024 at 11:39 AM CET, Sascha Hauer wrote:
> > > Hi Jules,
> > >
> > > On Thu, Nov 07, 2024 at 03:57:19PM +0100, Jules Maselbas wrote:
> > > > Clock driver adapted from Linux.
> > > > The pll-cpux is set to 816MHz and pll-periph0-2x is set to 1.2GHz.
> > >
> > > From which driver did you adapt this from? I tried copying
> > > drivers/clk/sunxi-ng/ccu-sun50i-a64.c from Linux over your driver and it
> > > looks quite different.
> > Yes this is adapted from drivers/clk/sunxi-ng/ccu-sun50i-a64.c, but it is very
> > different. This is a very stripped down version. I wanted to avoid having to
> > porting all the clock/gate/div code for sunxi and use as much as possible the
> > one available in barebox.
> >
> > > Would it be possible to get closer to the Linux driver? This would pay
> > > off once we want to do an update from Linux.
> > Humm...
> > I could try to get it closer, i would like to not having to copy all the ccu*
> > drivers, but maybe i could define barebox version of SUNXI_CCU_* macros
>
> In my experience Linux clk drivers fit quite nicely into barebox when
> just blindly throwing the code into it. I haven't got any experience
> with the allwinner clk code though, so your mileage may vary.

Allwinner clk driver doesn't uses any common clk such as generic div/mux/gate,
but redefines a lot of different small clk driver.

I tried to port the Allwinner clk into barebox, but it requires some changes:
 - get_parent function signatures doesn't match barebox's callback signatures
 - include <linux/delay.h> is missing and should include clock.h
 - clk drivers uses `struct notifier_block` which isn't supported
 - clk drivers uses get_rate_range which isn't supported either

> Putting the (mostly) unmodified versions into barebox has the advantage
> that updating from Linux is easy and adding new SoC support is easy as
> well.
>
> That said, it's fine to have heavily modified ckl driver versions in
> barebox and we can try that if you like. We can always revisit this
> decision should we have to.

I think that right now i don't have the energy to look further into porting
the Linux sunxi clock code to barebox.
I am fine with keeping this modified clk driver.
However, i would like for the V2 to put some functions into a common file
so they can be shared with other sunxi SoC.

Jules




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/6] clk: Add clock driver for sun50i-a64
  2024-11-12 15:58         ` Jules Maselbas
@ 2024-11-13  8:42           ` Sascha Hauer
  0 siblings, 0 replies; 16+ messages in thread
From: Sascha Hauer @ 2024-11-13  8:42 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Tue, Nov 12, 2024 at 04:58:49PM +0100, Jules Maselbas wrote:
> resent, as i forgot barebox ml in CC
> 
> On Fri Nov 8, 2024 at 1:59 PM CET, Sascha Hauer wrote:
> > On Fri, Nov 08, 2024 at 12:08:45PM +0100, Jules Maselbas wrote:
> > > Hi Sascha,
> > >
> > > On Fri Nov 8, 2024 at 11:39 AM CET, Sascha Hauer wrote:
> > > > Hi Jules,
> > > >
> > > > On Thu, Nov 07, 2024 at 03:57:19PM +0100, Jules Maselbas wrote:
> > > > > Clock driver adapted from Linux.
> > > > > The pll-cpux is set to 816MHz and pll-periph0-2x is set to 1.2GHz.
> > > >
> > > > From which driver did you adapt this from? I tried copying
> > > > drivers/clk/sunxi-ng/ccu-sun50i-a64.c from Linux over your driver and it
> > > > looks quite different.
> > > Yes this is adapted from drivers/clk/sunxi-ng/ccu-sun50i-a64.c, but it is very
> > > different. This is a very stripped down version. I wanted to avoid having to
> > > porting all the clock/gate/div code for sunxi and use as much as possible the
> > > one available in barebox.
> > >
> > > > Would it be possible to get closer to the Linux driver? This would pay
> > > > off once we want to do an update from Linux.
> > > Humm...
> > > I could try to get it closer, i would like to not having to copy all the ccu*
> > > drivers, but maybe i could define barebox version of SUNXI_CCU_* macros
> >
> > In my experience Linux clk drivers fit quite nicely into barebox when
> > just blindly throwing the code into it. I haven't got any experience
> > with the allwinner clk code though, so your mileage may vary.
> 
> Allwinner clk driver doesn't uses any common clk such as generic div/mux/gate,
> but redefines a lot of different small clk driver.
> 
> I tried to port the Allwinner clk into barebox, but it requires some changes:
>  - get_parent function signatures doesn't match barebox's callback signatures
>  - include <linux/delay.h> is missing and should include clock.h
>  - clk drivers uses `struct notifier_block` which isn't supported
>  - clk drivers uses get_rate_range which isn't supported either

Yes, there are some more, like we don't support determine_rate and
likely other things as well.

> 
> > Putting the (mostly) unmodified versions into barebox has the advantage
> > that updating from Linux is easy and adding new SoC support is easy as
> > well.
> >
> > That said, it's fine to have heavily modified ckl driver versions in
> > barebox and we can try that if you like. We can always revisit this
> > decision should we have to.
> 
> I think that right now i don't have the energy to look further into porting
> the Linux sunxi clock code to barebox.
> I am fine with keeping this modified clk driver.
> However, i would like for the V2 to put some functions into a common file
> so they can be shared with other sunxi SoC.

Ok, let's go for it.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/6] clk: divider: add error code propagation
  2024-11-07 14:57 ` [PATCH 2/6] clk: divider: add error code propagation Jules Maselbas
@ 2024-11-13  8:47   ` Ahmad Fatoum
  0 siblings, 0 replies; 16+ messages in thread
From: Ahmad Fatoum @ 2024-11-13  8:47 UTC (permalink / raw)
  To: Jules Maselbas, barebox

Hello Jules,

On 07.11.24 15:57, Jules Maselbas wrote:
> divider_get_val can return a negative error code and should not be used
> as a valid divider.
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
>  drivers/clk/clk-divider.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index ccab70aecc..b05b042da5 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
> @@ -308,6 +308,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
>  	struct clk *clk = clk_hw_to_clk(hw);
>  	struct clk_divider *divider = to_clk_divider(hw);
>  	unsigned int value;
> +	int ret;
>  	u32 val;
>  
>  	if (divider->flags & CLK_DIVIDER_READ_ONLY)
> @@ -320,8 +321,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
>  		clk_set_rate(clk_get_parent(clk), parent_rate);
>  	}
>  
> -	value = divider_get_val(rate, parent_rate, divider->table,
> +	value = ret = divider_get_val(rate, parent_rate, divider->table,
>  				divider->width, divider->flags);
> +	if (ret < 0)
> +		return ret;

Can't we just make value an int? This tidies up the code a bit at no
change of behavior.

Thanks,
Ahmad

>  
>  	val = readl(divider->reg);
>  	val &= ~(clk_div_mask(divider->width) << divider->shift);


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2024-11-13 21:04 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-07 14:57 [PATCH 0/6] Initial support for Allwinner A64 SoC Jules Maselbas
2024-11-07 14:57 ` [PATCH 1/6] clk: clk-composite: fix callback guard in clk_composite_round_rate Jules Maselbas
2024-11-07 15:05   ` Ahmad Fatoum
2024-11-08 10:21   ` (subset) " Sascha Hauer
2024-11-07 14:57 ` [PATCH 2/6] clk: divider: add error code propagation Jules Maselbas
2024-11-13  8:47   ` Ahmad Fatoum
2024-11-07 14:57 ` [PATCH 3/6] clk: Add clock driver for sun50i-a64 Jules Maselbas
2024-11-08 10:39   ` Sascha Hauer
2024-11-08 11:08     ` Jules Maselbas
2024-11-08 12:59       ` Sascha Hauer
2024-11-12 15:58         ` Jules Maselbas
2024-11-13  8:42           ` Sascha Hauer
2024-11-07 14:57 ` [PATCH 4/6] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
2024-11-08 10:48   ` Sascha Hauer
2024-11-07 14:57 ` [PATCH 5/6] mci: Add sunxi-mmc driver Jules Maselbas
2024-11-07 14:57 ` [PATCH 6/6] ARM: sunxi: Introduce mach-sunxi Jules Maselbas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox