diff options
Diffstat (limited to 'drivers/clk/sunxi-ng/ccu-sun50i-h6.c')
| -rw-r--r-- | drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 55 |
1 files changed, 44 insertions, 11 deletions
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c index bff446b78290..7fccda96d444 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c @@ -5,7 +5,7 @@ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @@ -95,13 +95,13 @@ static struct ccu_nkmp pll_periph1_clk = { }, }; +/* For GPU PLL, using an output divider for DFS causes system to fail */ #define SUN50I_H6_PLL_GPU_REG 0x030 static struct ccu_nkmp pll_gpu_clk = { .enable = BIT(31), .lock = BIT(28), .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ - .p = _SUNXI_CCU_DIV(0, 1), /* output divider */ .common = { .reg = 0x030, .hw.init = CLK_HW_INIT("pll-gpu", "osc24M", @@ -294,9 +294,9 @@ static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "psi-ahb1-ahb2", 0x62c, BIT(0), 0); +/* Keep GPU_CLK divider const to avoid DFS instability. */ static const char * const gpu_parents[] = { "pll-gpu" }; -static SUNXI_CCU_M_WITH_MUX_GATE(gpu_clk, "gpu", gpu_parents, 0x670, - 0, 3, /* M */ +static SUNXI_CCU_MUX_WITH_GATE(gpu_clk, "gpu", gpu_parents, 0x670, 24, 1, /* mux */ BIT(31), /* gate */ CLK_SET_RATE_PARENT); @@ -1076,7 +1076,7 @@ static struct clk_hw_onecell_data sun50i_h6_hw_clks = { .num = CLK_NUMBER, }; -static struct ccu_reset_map sun50i_h6_ccu_resets[] = { +static const struct ccu_reset_map sun50i_h6_ccu_resets[] = { [RST_MBUS] = { 0x540, BIT(30) }, [RST_BUS_DE] = { 0x60c, BIT(16) }, @@ -1181,18 +1181,37 @@ static const u32 usb2_clk_regs[] = { SUN50I_H6_USB3_CLK_REG, }; +static struct ccu_mux_nb sun50i_h6_cpu_nb = { + .common = &cpux_clk.common, + .cm = &cpux_clk.mux, + .delay_us = 1, + .bypass_index = 0, /* index of 24 MHz oscillator */ +}; + static int sun50i_h6_ccu_probe(struct platform_device *pdev) { - struct resource *res; void __iomem *reg; + int i, ret; u32 val; - int i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(&pdev->dev, res); + reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) return PTR_ERR(reg); + /* + * Force PLL_GPU output divider bits to 0 and adjust + * multiplier to sensible default value of 432 MHz. + */ + val = readl(reg + SUN50I_H6_PLL_GPU_REG); + val &= ~(GENMASK(15, 8) | BIT(0)); + val |= 17 << 8; + writel(val, reg + SUN50I_H6_PLL_GPU_REG); + + /* Force GPU_CLK divider bits to 0 */ + val = readl(reg + gpu_clk.common.reg); + val &= ~GENMASK(3, 0); + writel(val, reg + gpu_clk.common.reg); + /* Enable the lock bits on all PLLs */ for (i = 0; i < ARRAY_SIZE(pll_regs); i++) { val = readl(reg + pll_regs[i]); @@ -1240,19 +1259,33 @@ static int sun50i_h6_ccu_probe(struct platform_device *pdev) val |= BIT(24); writel(val, reg + SUN50I_H6_HDMI_CEC_CLK_REG); - return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_h6_ccu_desc); + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h6_ccu_desc); + if (ret) + return ret; + + /* Reparent CPU during PLL CPUX rate changes */ + ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, + &sun50i_h6_cpu_nb); + + return 0; } static const struct of_device_id sun50i_h6_ccu_ids[] = { { .compatible = "allwinner,sun50i-h6-ccu" }, { } }; +MODULE_DEVICE_TABLE(of, sun50i_h6_ccu_ids); static struct platform_driver sun50i_h6_ccu_driver = { .probe = sun50i_h6_ccu_probe, .driver = { .name = "sun50i-h6-ccu", + .suppress_bind_attrs = true, .of_match_table = sun50i_h6_ccu_ids, }, }; -builtin_platform_driver(sun50i_h6_ccu_driver); +module_platform_driver(sun50i_h6_ccu_driver); + +MODULE_IMPORT_NS("SUNXI_CCU"); +MODULE_DESCRIPTION("Support for the Allwinner H6 CCU"); +MODULE_LICENSE("GPL"); |
