summaryrefslogtreecommitdiff
path: root/drivers/clk/sunxi-ng
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/sunxi-ng')
-rw-r--r--drivers/clk/sunxi-ng/Kconfig5
-rw-r--r--drivers/clk/sunxi-ng/Makefile1
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c207
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h6-r.h19
4 files changed, 232 insertions, 0 deletions
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 79dfd296c3d1..826674d090fd 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -16,6 +16,11 @@ config SUN50I_H6_CCU
default ARM64 && ARCH_SUNXI
depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+config SUN50I_H6_R_CCU
+ bool "Support for the Allwinner H6 PRCM CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
config SUN4I_A10_CCU
bool "Support for the Allwinner A10/A20 CCU"
default MACH_SUN4I
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 128a40ee5c5e..acaa14cfa25c 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -23,6 +23,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o
# SoC support
obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o
obj-$(CONFIG_SUN50I_H6_CCU) += ccu-sun50i-h6.o
+obj-$(CONFIG_SUN50I_H6_R_CCU) += ccu-sun50i-h6-r.o
obj-$(CONFIG_SUN4I_A10_CCU) += ccu-sun4i-a10.o
obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o
obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
new file mode 100644
index 000000000000..27554eaf6929
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_nm.h"
+
+#include "ccu-sun50i-h6-r.h"
+
+/*
+ * Information about AR100 and AHB/APB clocks in R_CCU are gathered from
+ * clock definitions in the BSP source code.
+ */
+
+static const char * const ar100_r_apb2_parents[] = { "osc24M", "osc32k",
+ "pll-periph0", "iosc" };
+static const struct ccu_mux_var_prediv ar100_r_apb2_predivs[] = {
+ { .index = 2, .shift = 0, .width = 5 },
+};
+
+static struct ccu_div ar100_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .mux = {
+ .shift = 24,
+ .width = 2,
+
+ .var_predivs = ar100_r_apb2_predivs,
+ .n_var_predivs = ARRAY_SIZE(ar100_r_apb2_predivs),
+ },
+
+ .common = {
+ .reg = 0x000,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("ar100",
+ ar100_r_apb2_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CLK_FIXED_FACTOR(r_ahb_clk, "r-ahb", "ar100", 1, 1, 0);
+
+static struct ccu_div r_apb1_clk = {
+ .div = _SUNXI_CCU_DIV(0, 2),
+
+ .common = {
+ .reg = 0x00c,
+ .hw.init = CLK_HW_INIT("r-apb1",
+ "r-ahb",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div r_apb2_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .mux = {
+ .shift = 24,
+ .width = 2,
+
+ .var_predivs = ar100_r_apb2_predivs,
+ .n_var_predivs = ARRAY_SIZE(ar100_r_apb2_predivs),
+ },
+
+ .common = {
+ .reg = 0x010,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("r-apb2",
+ ar100_r_apb2_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+/*
+ * Information about the gate/resets are gathered from the clock header file
+ * in the BSP source code, although most of them are unused. The existence
+ * of the hardware block is verified with "3.1 Memory Mapping" chapter in
+ * "Allwinner H6 V200 User Manual V1.1"; and the parent APB buses are verified
+ * with "3.3.2.1 System Bus Tree" chapter inthe same document.
+ */
+static SUNXI_CCU_GATE(r_apb1_timer_clk, "r-apb1-timer", "r-apb1",
+ 0x11c, BIT(0), 0);
+static SUNXI_CCU_GATE(r_apb1_twd_clk, "r-apb1-twd", "r-apb1",
+ 0x12c, BIT(0), 0);
+static SUNXI_CCU_GATE(r_apb1_pwm_clk, "r-apb1-pwm", "r-apb1",
+ 0x13c, BIT(0), 0);
+static SUNXI_CCU_GATE(r_apb2_uart_clk, "r-apb2-uart", "r-apb2",
+ 0x18c, BIT(0), 0);
+static SUNXI_CCU_GATE(r_apb2_i2c_clk, "r-apb2-i2c", "r-apb2",
+ 0x19c, BIT(0), 0);
+static SUNXI_CCU_GATE(r_apb1_ir_clk, "r-apb1-ir", "r-apb1",
+ 0x1cc, BIT(0), 0);
+static SUNXI_CCU_GATE(r_apb1_w1_clk, "r-apb1-w1", "r-apb1",
+ 0x1cc, BIT(0), 0);
+
+/* Information of IR(RX) mod clock is gathered from BSP source code */
+static const char * const r_mod0_default_parents[] = { "osc32k", "osc24M" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(ir_clk, "ir",
+ r_mod0_default_parents, 0x1c0,
+ 0, 5, /* M */
+ 8, 2, /* P */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+/*
+ * BSP didn't use the 1-wire function at all now, and the information about
+ * this mod clock is guessed from the IR mod clock above. The existence of
+ * this mod clock is proven by BSP clock header, and the dividers are verified
+ * by contents in the 1-wire related chapter of the User Manual.
+ */
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(w1_clk, "w1",
+ r_mod0_default_parents, 0x1e0,
+ 0, 5, /* M */
+ 8, 2, /* P */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static struct ccu_common *sun50i_h6_r_ccu_clks[] = {
+ &ar100_clk.common,
+ &r_apb1_clk.common,
+ &r_apb2_clk.common,
+ &r_apb1_timer_clk.common,
+ &r_apb1_twd_clk.common,
+ &r_apb1_pwm_clk.common,
+ &r_apb2_uart_clk.common,
+ &r_apb2_i2c_clk.common,
+ &r_apb1_ir_clk.common,
+ &r_apb1_w1_clk.common,
+ &ir_clk.common,
+ &w1_clk.common,
+};
+
+static struct clk_hw_onecell_data sun50i_h6_r_hw_clks = {
+ .hws = {
+ [CLK_AR100] = &ar100_clk.common.hw,
+ [CLK_R_AHB] = &r_ahb_clk.hw,
+ [CLK_R_APB1] = &r_apb1_clk.common.hw,
+ [CLK_R_APB2] = &r_apb2_clk.common.hw,
+ [CLK_R_APB1_TIMER] = &r_apb1_timer_clk.common.hw,
+ [CLK_R_APB1_TWD] = &r_apb1_twd_clk.common.hw,
+ [CLK_R_APB1_PWM] = &r_apb1_pwm_clk.common.hw,
+ [CLK_R_APB2_UART] = &r_apb2_uart_clk.common.hw,
+ [CLK_R_APB2_I2C] = &r_apb2_i2c_clk.common.hw,
+ [CLK_R_APB1_IR] = &r_apb1_ir_clk.common.hw,
+ [CLK_R_APB1_W1] = &r_apb1_w1_clk.common.hw,
+ [CLK_IR] = &ir_clk.common.hw,
+ [CLK_W1] = &w1_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun50i_h6_r_ccu_resets[] = {
+ [RST_R_APB1_TIMER] = { 0x11c, BIT(16) },
+ [RST_R_APB1_TWD] = { 0x12c, BIT(16) },
+ [RST_R_APB1_PWM] = { 0x13c, BIT(16) },
+ [RST_R_APB2_UART] = { 0x18c, BIT(16) },
+ [RST_R_APB2_I2C] = { 0x19c, BIT(16) },
+ [RST_R_APB1_IR] = { 0x1cc, BIT(16) },
+ [RST_R_APB1_W1] = { 0x1ec, BIT(16) },
+};
+
+static const struct sunxi_ccu_desc sun50i_h6_r_ccu_desc = {
+ .ccu_clks = sun50i_h6_r_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun50i_h6_r_ccu_clks),
+
+ .hw_clks = &sun50i_h6_r_hw_clks,
+
+ .resets = sun50i_h6_r_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun50i_h6_r_ccu_resets),
+};
+
+static void __init sunxi_r_ccu_init(struct device_node *node,
+ const struct sunxi_ccu_desc *desc)
+{
+ void __iomem *reg;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%pOF: Could not map the clock registers\n", node);
+ return;
+ }
+
+ sunxi_ccu_probe(node, reg, desc);
+}
+
+static void __init sun50i_h6_r_ccu_setup(struct device_node *node)
+{
+ sunxi_r_ccu_init(node, &sun50i_h6_r_ccu_desc);
+}
+CLK_OF_DECLARE(sun50i_h6_r_ccu, "allwinner,sun50i-h6-r-ccu",
+ sun50i_h6_r_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.h b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.h
new file mode 100644
index 000000000000..782117dc0b28
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ */
+
+#ifndef _CCU_SUN50I_H6_R_H
+#define _CCU_SUN50I_H6_R_H
+
+#include <dt-bindings/clock/sun50i-h6-r-ccu.h>
+#include <dt-bindings/reset/sun50i-h6-r-ccu.h>
+
+/* AHB/APB bus clocks are not exported except APB1 for R_PIO */
+#define CLK_R_AHB 1
+
+#define CLK_R_APB2 3
+
+#define CLK_NUMBER (CLK_W1 + 1)
+
+#endif /* _CCU_SUN50I_H6_R_H */