mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-14 01:49:20 -04:00
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
This commit is contained in:
@@ -21,6 +21,7 @@ Required properties :
|
||||
- "allwinner,sun50i-a64-r-ccu"
|
||||
- "allwinner,sun50i-h5-ccu"
|
||||
- "allwinner,sun50i-h6-ccu"
|
||||
- "allwinner,sun50i-h6-r-ccu"
|
||||
- "nextthing,gr8-ccu"
|
||||
|
||||
- reg: Must contain the registers base address and length
|
||||
@@ -35,7 +36,7 @@ Required properties :
|
||||
For the main CCU on H6, one more clock is needed:
|
||||
- "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
|
||||
- "iosc": the SoC's internal frequency oscillator
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
207
drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
Normal file
207
drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
Normal file
@@ -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);
|
||||
19
drivers/clk/sunxi-ng/ccu-sun50i-h6-r.h
Normal file
19
drivers/clk/sunxi-ng/ccu-sun50i-h6-r.h
Normal file
@@ -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 */
|
||||
@@ -12,7 +12,8 @@
|
||||
*/
|
||||
|
||||
#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_reset.h"
|
||||
@@ -1250,17 +1251,45 @@ static struct ccu_mux_nb sun8i_r40_cpu_nb = {
|
||||
.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;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
|
||||
if (IS_ERR(reg)) {
|
||||
pr_err("%s: Could not map the clock registers\n",
|
||||
of_node_full_name(node));
|
||||
return;
|
||||
}
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
/* Force the PLL-Audio-1x divider to 4 */
|
||||
val = readl(reg + SUN8I_R40_PLL_AUDIO_REG);
|
||||
@@ -1277,7 +1306,14 @@ static void __init sun8i_r40_ccu_setup(struct device_node *node)
|
||||
val &= ~GENMASK(25, 20);
|
||||
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 */
|
||||
ccu_pll_notifier_register(&sun8i_r40_pll_cpu_nb);
|
||||
@@ -1285,6 +1321,20 @@ static void __init sun8i_r40_ccu_setup(struct device_node *node)
|
||||
/* Reparent CPU during PLL CPU rate changes */
|
||||
ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
|
||||
&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);
|
||||
|
||||
24
include/dt-bindings/clock/sun50i-h6-r-ccu.h
Normal file
24
include/dt-bindings/clock/sun50i-h6-r-ccu.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* 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_ */
|
||||
17
include/dt-bindings/reset/sun50i-h6-r-ccu.h
Normal file
17
include/dt-bindings/reset/sun50i-h6-r-ccu.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* 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_ */
|
||||
Reference in New Issue
Block a user