Commit f40cc01e authored by Stephen Boyd's avatar Stephen Boyd

Merge tag 'sunxi-clk-for-4.18' of...

Merge tag 'sunxi-clk-for-4.18' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into clk-allwinner

Pull Allwinner clock changes from Maxime Ripard:

Not a lot of changes for this release, but two quite important features
were added: the H6 PRCM clock support, and the needed changes to the R40
clock driver to allow for the EMAC to operate.

* tag 'sunxi-clk-for-4.18' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux:
  clk: sunxi-ng: r40: export a regmap to access the GMAC register
  clk: sunxi-ng: r40: rewrite init code to a platform driver
  clk: sunxi-ng: add support for H6 PRCM CCU
parents 60cc43fc 17de4c85
...@@ -21,6 +21,7 @@ Required properties : ...@@ -21,6 +21,7 @@ Required properties :
- "allwinner,sun50i-a64-r-ccu" - "allwinner,sun50i-a64-r-ccu"
- "allwinner,sun50i-h5-ccu" - "allwinner,sun50i-h5-ccu"
- "allwinner,sun50i-h6-ccu" - "allwinner,sun50i-h6-ccu"
- "allwinner,sun50i-h6-r-ccu"
- "nextthing,gr8-ccu" - "nextthing,gr8-ccu"
- reg: Must contain the registers base address and length - reg: Must contain the registers base address and length
...@@ -35,7 +36,7 @@ Required properties : ...@@ -35,7 +36,7 @@ Required properties :
For the main CCU on H6, one more clock is needed: For the main CCU on H6, one more clock is needed:
- "iosc": the SoC's internal frequency oscillator - "iosc": the SoC's internal frequency oscillator
For the PRCM CCUs on A83T/H3/A64, two more clocks are needed: For the PRCM CCUs on A83T/H3/A64/H6, two more clocks are needed:
- "pll-periph": the SoC's peripheral PLL from the main CCU - "pll-periph": the SoC's peripheral PLL from the main CCU
- "iosc": the SoC's internal frequency oscillator - "iosc": the SoC's internal frequency oscillator
......
...@@ -16,6 +16,11 @@ config SUN50I_H6_CCU ...@@ -16,6 +16,11 @@ config SUN50I_H6_CCU
default ARM64 && ARCH_SUNXI default ARM64 && ARCH_SUNXI
depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST 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 config SUN4I_A10_CCU
bool "Support for the Allwinner A10/A20 CCU" bool "Support for the Allwinner A10/A20 CCU"
default MACH_SUN4I default MACH_SUN4I
......
...@@ -23,6 +23,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o ...@@ -23,6 +23,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o
# SoC support # SoC support
obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o
obj-$(CONFIG_SUN50I_H6_CCU) += ccu-sun50i-h6.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_SUN4I_A10_CCU) += ccu-sun4i-a10.o
obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o
obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o
......
// 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);
/* 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 */
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/of_address.h> #include <linux/platform_device.h>
#include <linux/regmap.h>
#include "ccu_common.h" #include "ccu_common.h"
#include "ccu_reset.h" #include "ccu_reset.h"
...@@ -1250,17 +1251,45 @@ static struct ccu_mux_nb sun8i_r40_cpu_nb = { ...@@ -1250,17 +1251,45 @@ static struct ccu_mux_nb sun8i_r40_cpu_nb = {
.bypass_index = 1, /* index of 24 MHz oscillator */ .bypass_index = 1, /* index of 24 MHz oscillator */
}; };
static void __init sun8i_r40_ccu_setup(struct device_node *node) /*
* Add a regmap for the GMAC driver (dwmac-sun8i) to access the
* GMAC configuration register.
* Only this register is allowed to be written, in order to
* prevent overriding critical clock configuration.
*/
#define SUN8I_R40_GMAC_CFG_REG 0x164
static bool sun8i_r40_ccu_regmap_accessible_reg(struct device *dev,
unsigned int reg)
{
if (reg == SUN8I_R40_GMAC_CFG_REG)
return true;
return false;
}
static struct regmap_config sun8i_r40_ccu_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0x320, /* PLL_LOCK_CTRL_REG */
/* other devices have no business accessing other registers */
.readable_reg = sun8i_r40_ccu_regmap_accessible_reg,
.writeable_reg = sun8i_r40_ccu_regmap_accessible_reg,
};
static int sun8i_r40_ccu_probe(struct platform_device *pdev)
{ {
struct resource *res;
struct regmap *regmap;
void __iomem *reg; void __iomem *reg;
u32 val; u32 val;
int ret;
reg = of_io_request_and_map(node, 0, of_node_full_name(node)); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (IS_ERR(reg)) { reg = devm_ioremap_resource(&pdev->dev, res);
pr_err("%s: Could not map the clock registers\n", if (IS_ERR(reg))
of_node_full_name(node)); return PTR_ERR(reg);
return;
}
/* Force the PLL-Audio-1x divider to 4 */ /* Force the PLL-Audio-1x divider to 4 */
val = readl(reg + SUN8I_R40_PLL_AUDIO_REG); val = readl(reg + SUN8I_R40_PLL_AUDIO_REG);
...@@ -1277,7 +1306,14 @@ static void __init sun8i_r40_ccu_setup(struct device_node *node) ...@@ -1277,7 +1306,14 @@ static void __init sun8i_r40_ccu_setup(struct device_node *node)
val &= ~GENMASK(25, 20); val &= ~GENMASK(25, 20);
writel(val, reg + SUN8I_R40_USB_CLK_REG); writel(val, reg + SUN8I_R40_USB_CLK_REG);
sunxi_ccu_probe(node, reg, &sun8i_r40_ccu_desc); regmap = devm_regmap_init_mmio(&pdev->dev, reg,
&sun8i_r40_ccu_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_r40_ccu_desc);
if (ret)
return ret;
/* Gate then ungate PLL CPU after any rate changes */ /* Gate then ungate PLL CPU after any rate changes */
ccu_pll_notifier_register(&sun8i_r40_pll_cpu_nb); ccu_pll_notifier_register(&sun8i_r40_pll_cpu_nb);
...@@ -1285,6 +1321,20 @@ static void __init sun8i_r40_ccu_setup(struct device_node *node) ...@@ -1285,6 +1321,20 @@ static void __init sun8i_r40_ccu_setup(struct device_node *node)
/* Reparent CPU during PLL CPU rate changes */ /* Reparent CPU during PLL CPU rate changes */
ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk, ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
&sun8i_r40_cpu_nb); &sun8i_r40_cpu_nb);
return 0;
} }
CLK_OF_DECLARE(sun8i_r40_ccu, "allwinner,sun8i-r40-ccu",
sun8i_r40_ccu_setup); static const struct of_device_id sun8i_r40_ccu_ids[] = {
{ .compatible = "allwinner,sun8i-r40-ccu" },
{ }
};
static struct platform_driver sun8i_r40_ccu_driver = {
.probe = sun8i_r40_ccu_probe,
.driver = {
.name = "sun8i-r40-ccu",
.of_match_table = sun8i_r40_ccu_ids,
},
};
builtin_platform_driver(sun8i_r40_ccu_driver);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.xyz>
*/
#ifndef _DT_BINDINGS_CLK_SUN50I_H6_R_CCU_H_
#define _DT_BINDINGS_CLK_SUN50I_H6_R_CCU_H_
#define CLK_AR100 0
#define CLK_R_APB1 2
#define CLK_R_APB1_TIMER 4
#define CLK_R_APB1_TWD 5
#define CLK_R_APB1_PWM 6
#define CLK_R_APB2_UART 7
#define CLK_R_APB2_I2C 8
#define CLK_R_APB1_IR 9
#define CLK_R_APB1_W1 10
#define CLK_IR 11
#define CLK_W1 12
#endif /* _DT_BINDINGS_CLK_SUN50I_H6_R_CCU_H_ */
/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
/*
* Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.xyz>
*/
#ifndef _DT_BINDINGS_RST_SUN50I_H6_R_CCU_H_
#define _DT_BINDINGS_RST_SUN50I_H6_R_CCU_H_
#define RST_R_APB1_TIMER 0
#define RST_R_APB1_TWD 1
#define RST_R_APB1_PWM 2
#define RST_R_APB2_UART 3
#define RST_R_APB2_I2C 4
#define RST_R_APB1_IR 5
#define RST_R_APB1_W1 6
#endif /* _DT_BINDINGS_RST_SUN50I_H6_R_CCU_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment