clk: Add Allwinner A64 CLK driver
authorJagan Teki <jagan@amarulasolutions.com>
Sat, 22 Dec 2018 16:02:49 +0000 (21:32 +0530)
committerJagan Teki <jagan@amarulasolutions.com>
Fri, 18 Jan 2019 16:49:08 +0000 (22:19 +0530)
Add initial clock driver for Allwinner A64.

Implement USB clock enable and disable functions for
OHCI, EHCI, OTG and USBPHY gate and clock registers
via ccu clk gate table.

Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
arch/arm/include/asm/arch-sunxi/ccu.h [new file with mode: 0644]
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/sunxi/Kconfig [new file with mode: 0644]
drivers/clk/sunxi/Makefile [new file with mode: 0644]
drivers/clk/sunxi/clk_a64.c [new file with mode: 0644]
drivers/clk/sunxi/clk_sunxi.c [new file with mode: 0644]

diff --git a/arch/arm/include/asm/arch-sunxi/ccu.h b/arch/arm/include/asm/arch-sunxi/ccu.h
new file mode 100644 (file)
index 0000000..24efe0a
--- /dev/null
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Amarula Solutions.
+ * Author: Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#ifndef _ASM_ARCH_CCU_H
+#define _ASM_ARCH_CCU_H
+
+/**
+ * enum ccu_flags - ccu clock flags
+ *
+ * @CCU_CLK_F_IS_VALID:                is given clock gate is valid?
+ */
+enum ccu_flags {
+       CCU_CLK_F_IS_VALID              = BIT(0),
+};
+
+/**
+ * struct ccu_clk_gate - ccu clock gate
+ * @off:       gate offset
+ * @bit:       gate bit
+ * @flags:     ccu clock gate flags
+ */
+struct ccu_clk_gate {
+       u16 off;
+       u32 bit;
+       enum ccu_flags flags;
+};
+
+#define GATE(_off, _bit) {                     \
+       .off = _off,                            \
+       .bit = _bit,                            \
+       .flags = CCU_CLK_F_IS_VALID,            \
+}
+
+/**
+ * struct ccu_desc - clock control unit descriptor
+ *
+ * @gates:     clock gates
+ */
+struct ccu_desc {
+       const struct ccu_clk_gate *gates;
+};
+
+/**
+ * struct ccu_priv - sunxi clock control unit
+ *
+ * @base:      base address
+ * @desc:      ccu descriptor
+ */
+struct ccu_priv {
+       void *base;
+       const struct ccu_desc *desc;
+};
+
+/**
+ * sunxi_clk_probe - common sunxi clock probe
+ * @dev:       clock device
+ */
+int sunxi_clk_probe(struct udevice *dev);
+
+extern struct clk_ops sunxi_clk_ops;
+
+#endif /* _ASM_ARCH_CCU_H */
index eadf7f8..51c931b 100644 (file)
@@ -104,6 +104,7 @@ source "drivers/clk/imx/Kconfig"
 source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/owl/Kconfig"
 source "drivers/clk/renesas/Kconfig"
+source "drivers/clk/sunxi/Kconfig"
 source "drivers/clk/tegra/Kconfig"
 source "drivers/clk/uniphier/Kconfig"
 
index 9acbb1a..6a4ff91 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
 obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
 obj-$(CONFIG_CLK_OWL) += owl/
 obj-$(CONFIG_CLK_RENESAS) += renesas/
+obj-$(CONFIG_ARCH_SUNXI) += sunxi/
 obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o
 obj-$(CONFIG_CLK_STM32MP1) += clk_stm32mp1.o
 obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
new file mode 100644 (file)
index 0000000..bf5ecb3
--- /dev/null
@@ -0,0 +1,18 @@
+config CLK_SUNXI
+       bool "Clock support for Allwinner SoCs"
+       depends on CLK && ARCH_SUNXI
+       default y
+       help
+         This enables support for common clock driver API on Allwinner
+         SoCs.
+
+if CLK_SUNXI
+
+config CLK_SUN50I_A64
+       bool "Clock driver for Allwinner A64"
+       default MACH_SUN50I
+       help
+         This enables common clock driver support for platforms based
+         on Allwinner A64 SoC.
+
+endif # CLK_SUNXI
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644 (file)
index 0000000..fb20d28
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Copyright (C) 2018 Amarula Solutions.
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+obj-$(CONFIG_CLK_SUNXI) += clk_sunxi.o
+
+obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o
diff --git a/drivers/clk/sunxi/clk_a64.c b/drivers/clk/sunxi/clk_a64.c
new file mode 100644 (file)
index 0000000..803a2f7
--- /dev/null
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Amarula Solutions.
+ * Author: Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/arch/ccu.h>
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+
+static const struct ccu_clk_gate a64_gates[] = {
+       [CLK_BUS_OTG]           = GATE(0x060, BIT(23)),
+       [CLK_BUS_EHCI0]         = GATE(0x060, BIT(24)),
+       [CLK_BUS_EHCI1]         = GATE(0x060, BIT(25)),
+       [CLK_BUS_OHCI0]         = GATE(0x060, BIT(28)),
+       [CLK_BUS_OHCI1]         = GATE(0x060, BIT(29)),
+
+       [CLK_USB_PHY0]          = GATE(0x0cc, BIT(8)),
+       [CLK_USB_PHY1]          = GATE(0x0cc, BIT(9)),
+       [CLK_USB_HSIC]          = GATE(0x0cc, BIT(10)),
+       [CLK_USB_HSIC_12M]      = GATE(0x0cc, BIT(11)),
+       [CLK_USB_OHCI0]         = GATE(0x0cc, BIT(16)),
+       [CLK_USB_OHCI1]         = GATE(0x0cc, BIT(17)),
+};
+
+static const struct ccu_desc a64_ccu_desc = {
+       .gates = a64_gates,
+};
+
+static const struct udevice_id a64_ccu_ids[] = {
+       { .compatible = "allwinner,sun50i-a64-ccu",
+         .data = (ulong)&a64_ccu_desc },
+       { }
+};
+
+U_BOOT_DRIVER(clk_sun50i_a64) = {
+       .name           = "sun50i_a64_ccu",
+       .id             = UCLASS_CLK,
+       .of_match       = a64_ccu_ids,
+       .priv_auto_alloc_size   = sizeof(struct ccu_priv),
+       .ops            = &sunxi_clk_ops,
+       .probe          = sunxi_clk_probe,
+};
diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
new file mode 100644 (file)
index 0000000..62ce299
--- /dev/null
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Amarula Solutions.
+ * Author: Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/ccu.h>
+#include <linux/log2.h>
+
+static const struct ccu_clk_gate *priv_to_gate(struct ccu_priv *priv,
+                                              unsigned long id)
+{
+       return &priv->desc->gates[id];
+}
+
+static int sunxi_set_gate(struct clk *clk, bool on)
+{
+       struct ccu_priv *priv = dev_get_priv(clk->dev);
+       const struct ccu_clk_gate *gate = priv_to_gate(priv, clk->id);
+       u32 reg;
+
+       if (!(gate->flags & CCU_CLK_F_IS_VALID)) {
+               printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id);
+               return 0;
+       }
+
+       debug("%s: (CLK#%ld) off#0x%x, BIT(%d)\n", __func__,
+             clk->id, gate->off, ilog2(gate->bit));
+
+       reg = readl(priv->base + gate->off);
+       if (on)
+               reg |= gate->bit;
+       else
+               reg &= ~gate->bit;
+
+       writel(reg, priv->base + gate->off);
+
+       return 0;
+}
+
+static int sunxi_clk_enable(struct clk *clk)
+{
+       return sunxi_set_gate(clk, true);
+}
+
+static int sunxi_clk_disable(struct clk *clk)
+{
+       return sunxi_set_gate(clk, false);
+}
+
+struct clk_ops sunxi_clk_ops = {
+       .enable = sunxi_clk_enable,
+       .disable = sunxi_clk_disable,
+};
+
+int sunxi_clk_probe(struct udevice *dev)
+{
+       struct ccu_priv *priv = dev_get_priv(dev);
+
+       priv->base = dev_read_addr_ptr(dev);
+       if (!priv->base)
+               return -ENOMEM;
+
+       priv->desc = (const struct ccu_desc *)dev_get_driver_data(dev);
+       if (!priv->desc)
+               return -EINVAL;
+
+       return 0;
+}