Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'sunxi-clocks-for-4.5' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into clk-next

Allwinner clocks changes for 4.5

Clock patches for the Allwinner SoCs:
- H3 clocks
- A10/A20 Video Engine clocks
- DRAM gates
- A80 special CPU clock

+638 -12
+10
Documentation/devicetree/bindings/clock/sunxi.txt
··· 27 27 "allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s 28 28 "allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20 29 29 "allwinner,sun6i-a31-ar100-clk" - for the AR100 on A31 30 + "allwinner,sun9i-a80-cpus-clk" - for the CPUS on A80 30 31 "allwinner,sun6i-a31-ahb1-clk" - for the AHB1 clock on A31 32 + "allwinner,sun8i-h3-ahb2-clk" - for the AHB2 clock on H3 31 33 "allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31 32 34 "allwinner,sun8i-a23-ahb1-gates-clk" - for the AHB1 gates on A23 33 35 "allwinner,sun9i-a80-ahb0-gates-clk" - for the AHB0 gates on A80 ··· 57 55 "allwinner,sun9i-a80-apb1-gates-clk" - for the APB1 gates on A80 58 56 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 59 57 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 58 + "allwinner,sun8i-h3-bus-gates-clk" - for the bus gates on H3 59 + "allwinner,sun9i-a80-apbs-gates-clk" - for the APBS gates on A80 60 + "allwinner,sun4i-a10-dram-gates-clk" - for the DRAM gates on A10 60 61 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 61 62 "allwinner,sun4i-a10-mmc-clk" - for the MMC clock 62 63 "allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80 ··· 73 68 "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 74 69 "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31 75 70 "allwinner,sun8i-a23-usb-clk" - for usb gates + resets on A23 71 + "allwinner,sun8i-h3-usb-clk" - for usb gates + resets on H3 76 72 "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 77 73 "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 74 + "allwinner,sun4i-a10-ve-clk" - for the Video Engine clock 78 75 79 76 Required properties for all clocks: 80 77 - reg : shall be the control register address for the clock. ··· 95 88 96 89 And "allwinner,*-usb-clk" clocks also require: 97 90 - reset-cells : shall be set to 1 91 + 92 + The "allwinner,sun4i-a10-ve-clk" clock also requires: 93 + - reset-cells : shall be set to 0 98 94 99 95 The "allwinner,sun9i-a80-mmc-config-clk" clock also requires: 100 96 - #reset-cells : shall be set to 1
+5
drivers/clk/sunxi/Makefile
··· 7 7 obj-y += clk-a10-hosc.o 8 8 obj-y += clk-a10-mod1.o 9 9 obj-y += clk-a10-pll2.o 10 + obj-y += clk-a10-ve.o 10 11 obj-y += clk-a20-gmac.o 11 12 obj-y += clk-mod0.o 12 13 obj-y += clk-simple-gates.o 14 + obj-y += clk-sun8i-bus-gates.o 13 15 obj-y += clk-sun8i-mbus.o 14 16 obj-y += clk-sun9i-core.o 15 17 obj-y += clk-sun9i-mmc.o 16 18 obj-y += clk-usb.o 19 + 20 + obj-$(CONFIG_MACH_SUN9I) += clk-sun8i-apb0.o 21 + obj-$(CONFIG_MACH_SUN9I) += clk-sun9i-cpus.o 17 22 18 23 obj-$(CONFIG_MFD_SUN6I_PRCM) += \ 19 24 clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \
+171
drivers/clk/sunxi/clk-a10-ve.c
··· 1 + /* 2 + * Copyright 2015 Chen-Yu Tsai 3 + * 4 + * Chen-Yu Tsai <wens@csie.org> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/clk-provider.h> 18 + #include <linux/of.h> 19 + #include <linux/of_address.h> 20 + #include <linux/reset-controller.h> 21 + #include <linux/slab.h> 22 + #include <linux/spinlock.h> 23 + 24 + static DEFINE_SPINLOCK(ve_lock); 25 + 26 + #define SUN4I_VE_ENABLE 31 27 + #define SUN4I_VE_DIVIDER_SHIFT 16 28 + #define SUN4I_VE_DIVIDER_WIDTH 3 29 + #define SUN4I_VE_RESET 0 30 + 31 + /** 32 + * sunxi_ve_reset... - reset bit in ve clk registers handling 33 + */ 34 + 35 + struct ve_reset_data { 36 + void __iomem *reg; 37 + spinlock_t *lock; 38 + struct reset_controller_dev rcdev; 39 + }; 40 + 41 + static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, 42 + unsigned long id) 43 + { 44 + struct ve_reset_data *data = container_of(rcdev, 45 + struct ve_reset_data, 46 + rcdev); 47 + unsigned long flags; 48 + u32 reg; 49 + 50 + spin_lock_irqsave(data->lock, flags); 51 + 52 + reg = readl(data->reg); 53 + writel(reg & ~BIT(SUN4I_VE_RESET), data->reg); 54 + 55 + spin_unlock_irqrestore(data->lock, flags); 56 + 57 + return 0; 58 + } 59 + 60 + static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, 61 + unsigned long id) 62 + { 63 + struct ve_reset_data *data = container_of(rcdev, 64 + struct ve_reset_data, 65 + rcdev); 66 + unsigned long flags; 67 + u32 reg; 68 + 69 + spin_lock_irqsave(data->lock, flags); 70 + 71 + reg = readl(data->reg); 72 + writel(reg | BIT(SUN4I_VE_RESET), data->reg); 73 + 74 + spin_unlock_irqrestore(data->lock, flags); 75 + 76 + return 0; 77 + } 78 + 79 + static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, 80 + const struct of_phandle_args *reset_spec) 81 + { 82 + if (WARN_ON(reset_spec->args_count != 0)) 83 + return -EINVAL; 84 + 85 + return 0; 86 + } 87 + 88 + static struct reset_control_ops sunxi_ve_reset_ops = { 89 + .assert = sunxi_ve_reset_assert, 90 + .deassert = sunxi_ve_reset_deassert, 91 + }; 92 + 93 + static void __init sun4i_ve_clk_setup(struct device_node *node) 94 + { 95 + struct clk *clk; 96 + struct clk_divider *div; 97 + struct clk_gate *gate; 98 + struct ve_reset_data *reset_data; 99 + const char *parent; 100 + const char *clk_name = node->name; 101 + void __iomem *reg; 102 + int err; 103 + 104 + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 105 + if (IS_ERR(reg)) 106 + return; 107 + 108 + div = kzalloc(sizeof(*div), GFP_KERNEL); 109 + if (!div) 110 + goto err_unmap; 111 + 112 + gate = kzalloc(sizeof(*gate), GFP_KERNEL); 113 + if (!gate) 114 + goto err_free_div; 115 + 116 + of_property_read_string(node, "clock-output-names", &clk_name); 117 + parent = of_clk_get_parent_name(node, 0); 118 + 119 + gate->reg = reg; 120 + gate->bit_idx = SUN4I_VE_ENABLE; 121 + gate->lock = &ve_lock; 122 + 123 + div->reg = reg; 124 + div->shift = SUN4I_VE_DIVIDER_SHIFT; 125 + div->width = SUN4I_VE_DIVIDER_WIDTH; 126 + div->lock = &ve_lock; 127 + 128 + clk = clk_register_composite(NULL, clk_name, &parent, 1, 129 + NULL, NULL, 130 + &div->hw, &clk_divider_ops, 131 + &gate->hw, &clk_gate_ops, 132 + CLK_SET_RATE_PARENT); 133 + if (IS_ERR(clk)) 134 + goto err_free_gate; 135 + 136 + err = of_clk_add_provider(node, of_clk_src_simple_get, clk); 137 + if (err) 138 + goto err_unregister_clk; 139 + 140 + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 141 + if (!reset_data) 142 + goto err_del_provider; 143 + 144 + reset_data->reg = reg; 145 + reset_data->lock = &ve_lock; 146 + reset_data->rcdev.nr_resets = 1; 147 + reset_data->rcdev.ops = &sunxi_ve_reset_ops; 148 + reset_data->rcdev.of_node = node; 149 + reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; 150 + reset_data->rcdev.of_reset_n_cells = 0; 151 + err = reset_controller_register(&reset_data->rcdev); 152 + if (err) 153 + goto err_free_reset; 154 + 155 + return; 156 + 157 + err_free_reset: 158 + kfree(reset_data); 159 + err_del_provider: 160 + of_clk_del_provider(node); 161 + err_unregister_clk: 162 + clk_unregister(clk); 163 + err_free_gate: 164 + kfree(gate); 165 + err_free_div: 166 + kfree(div); 167 + err_unmap: 168 + iounmap(reg); 169 + } 170 + CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk", 171 + sun4i_ve_clk_setup);
+14
drivers/clk/sunxi/clk-simple-gates.c
··· 140 140 sunxi_simple_gates_init); 141 141 CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-gates-clk", 142 142 sunxi_simple_gates_init); 143 + CLK_OF_DECLARE(sun9i_a80_apbs, "allwinner,sun9i-a80-apbs-gates-clk", 144 + sunxi_simple_gates_init); 143 145 144 146 static const int sun4i_a10_ahb_critical_clocks[] __initconst = { 145 147 14, /* ahb_sdram */ ··· 160 158 sun4i_a10_ahb_init); 161 159 CLK_OF_DECLARE(sun7i_a20_ahb, "allwinner,sun7i-a20-ahb-gates-clk", 162 160 sun4i_a10_ahb_init); 161 + 162 + static const int sun4i_a10_dram_critical_clocks[] __initconst = { 163 + 15, /* dram_output */ 164 + }; 165 + 166 + static void __init sun4i_a10_dram_init(struct device_node *node) 167 + { 168 + sunxi_simple_gates_setup(node, sun4i_a10_dram_critical_clocks, 169 + ARRAY_SIZE(sun4i_a10_dram_critical_clocks)); 170 + } 171 + CLK_OF_DECLARE(sun4i_a10_dram, "allwinner,sun4i-a10-dram-gates-clk", 172 + sun4i_a10_dram_init);
+68 -12
drivers/clk/sunxi/clk-sun8i-apb0.c
··· 17 17 #include <linux/clk-provider.h> 18 18 #include <linux/module.h> 19 19 #include <linux/of.h> 20 + #include <linux/of_address.h> 20 21 #include <linux/platform_device.h> 22 + 23 + static struct clk *sun8i_a23_apb0_register(struct device_node *node, 24 + void __iomem *reg) 25 + { 26 + const char *clk_name = node->name; 27 + const char *clk_parent; 28 + struct clk *clk; 29 + int ret; 30 + 31 + clk_parent = of_clk_get_parent_name(node, 0); 32 + if (!clk_parent) 33 + return ERR_PTR(-EINVAL); 34 + 35 + of_property_read_string(node, "clock-output-names", &clk_name); 36 + 37 + /* The A23 APB0 clock is a standard 2 bit wide divider clock */ 38 + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, 39 + 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL); 40 + if (IS_ERR(clk)) 41 + return clk; 42 + 43 + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); 44 + if (ret) 45 + goto err_unregister; 46 + 47 + return clk; 48 + 49 + err_unregister: 50 + clk_unregister_divider(clk); 51 + 52 + return ERR_PTR(ret); 53 + } 54 + 55 + static void sun8i_a23_apb0_setup(struct device_node *node) 56 + { 57 + void __iomem *reg; 58 + struct resource res; 59 + struct clk *clk; 60 + 61 + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 62 + if (IS_ERR(reg)) { 63 + /* 64 + * This happens with clk nodes instantiated through mfd, 65 + * as those do not have their resources assigned in the 66 + * device tree. Do not print an error in this case. 67 + */ 68 + if (PTR_ERR(reg) != -EINVAL) 69 + pr_err("Could not get registers for a23-apb0-clk\n"); 70 + 71 + return; 72 + } 73 + 74 + clk = sun8i_a23_apb0_register(node, reg); 75 + if (IS_ERR(clk)) 76 + goto err_unmap; 77 + 78 + return; 79 + 80 + err_unmap: 81 + iounmap(reg); 82 + of_address_to_resource(node, 0, &res); 83 + release_mem_region(res.start, resource_size(&res)); 84 + } 85 + CLK_OF_DECLARE(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk", 86 + sun8i_a23_apb0_setup); 21 87 22 88 static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev) 23 89 { 24 90 struct device_node *np = pdev->dev.of_node; 25 - const char *clk_name = np->name; 26 - const char *clk_parent; 27 91 struct resource *r; 28 92 void __iomem *reg; 29 93 struct clk *clk; ··· 97 33 if (IS_ERR(reg)) 98 34 return PTR_ERR(reg); 99 35 100 - clk_parent = of_clk_get_parent_name(np, 0); 101 - if (!clk_parent) 102 - return -EINVAL; 103 - 104 - of_property_read_string(np, "clock-output-names", &clk_name); 105 - 106 - /* The A23 APB0 clock is a standard 2 bit wide divider clock */ 107 - clk = clk_register_divider(&pdev->dev, clk_name, clk_parent, 0, reg, 108 - 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL); 36 + clk = sun8i_a23_apb0_register(np, reg); 109 37 if (IS_ERR(clk)) 110 38 return PTR_ERR(clk); 111 39 112 - return of_clk_add_provider(np, of_clk_src_simple_get, clk); 40 + return 0; 113 41 } 114 42 115 43 static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = {
+112
drivers/clk/sunxi/clk-sun8i-bus-gates.c
··· 1 + /* 2 + * Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com> 3 + * 4 + * Based on clk-simple-gates.c, which is: 5 + * Copyright 2015 Maxime Ripard 6 + * 7 + * Maxime Ripard <maxime.ripard@free-electrons.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License as published by 11 + * the Free Software Foundation; either version 2 of the License, or 12 + * (at your option) any later version. 13 + * 14 + * This program is distributed in the hope that it will be useful, 15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 + * GNU General Public License for more details. 18 + */ 19 + 20 + #include <linux/clk.h> 21 + #include <linux/clk-provider.h> 22 + #include <linux/of.h> 23 + #include <linux/of_address.h> 24 + #include <linux/slab.h> 25 + #include <linux/spinlock.h> 26 + 27 + static DEFINE_SPINLOCK(gates_lock); 28 + 29 + static void __init sun8i_h3_bus_gates_init(struct device_node *node) 30 + { 31 + static const char * const names[] = { "ahb1", "ahb2", "apb1", "apb2" }; 32 + enum { AHB1, AHB2, APB1, APB2, PARENT_MAX } clk_parent; 33 + const char *parents[PARENT_MAX]; 34 + struct clk_onecell_data *clk_data; 35 + const char *clk_name; 36 + struct property *prop; 37 + struct resource res; 38 + void __iomem *clk_reg; 39 + void __iomem *reg; 40 + const __be32 *p; 41 + int number, i; 42 + u8 clk_bit; 43 + int index; 44 + 45 + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 46 + if (IS_ERR(reg)) 47 + return; 48 + 49 + for (i = 0; i < ARRAY_SIZE(names); i++) { 50 + int idx = of_property_match_string(node, "clock-names", 51 + names[i]); 52 + if (idx < 0) 53 + return; 54 + 55 + parents[i] = of_clk_get_parent_name(node, idx); 56 + } 57 + 58 + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); 59 + if (!clk_data) 60 + goto err_unmap; 61 + 62 + number = of_property_count_u32_elems(node, "clock-indices"); 63 + of_property_read_u32_index(node, "clock-indices", number - 1, &number); 64 + 65 + clk_data->clks = kcalloc(number + 1, sizeof(struct clk *), GFP_KERNEL); 66 + if (!clk_data->clks) 67 + goto err_free_data; 68 + 69 + i = 0; 70 + of_property_for_each_u32(node, "clock-indices", prop, p, index) { 71 + of_property_read_string_index(node, "clock-output-names", 72 + i, &clk_name); 73 + 74 + if (index == 17 || (index >= 29 && index <= 31)) 75 + clk_parent = AHB2; 76 + else if (index <= 63 || index >= 128) 77 + clk_parent = AHB1; 78 + else if (index >= 64 && index <= 95) 79 + clk_parent = APB1; 80 + else if (index >= 96 && index <= 127) 81 + clk_parent = APB2; 82 + 83 + clk_reg = reg + 4 * (index / 32); 84 + clk_bit = index % 32; 85 + 86 + clk_data->clks[index] = clk_register_gate(NULL, clk_name, 87 + parents[clk_parent], 88 + 0, clk_reg, clk_bit, 89 + 0, &gates_lock); 90 + i++; 91 + 92 + if (IS_ERR(clk_data->clks[index])) { 93 + WARN_ON(true); 94 + continue; 95 + } 96 + } 97 + 98 + clk_data->clk_num = number + 1; 99 + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); 100 + 101 + return; 102 + 103 + err_free_data: 104 + kfree(clk_data); 105 + err_unmap: 106 + iounmap(reg); 107 + of_address_to_resource(node, 0, &res); 108 + release_mem_region(res.start, resource_size(&res)); 109 + } 110 + 111 + CLK_OF_DECLARE(sun8i_h3_bus_gates, "allwinner,sun8i-h3-bus-gates-clk", 112 + sun8i_h3_bus_gates_init);
+240
drivers/clk/sunxi/clk-sun9i-cpus.c
··· 1 + /* 2 + * Copyright (C) 2015 Chen-Yu Tsai 3 + * 4 + * Chen-Yu Tsai <wens@csie.org> 5 + * 6 + * Allwinner A80 CPUS clock driver 7 + * 8 + */ 9 + 10 + #include <linux/clk.h> 11 + #include <linux/clk-provider.h> 12 + #include <linux/slab.h> 13 + #include <linux/spinlock.h> 14 + #include <linux/of.h> 15 + #include <linux/of_address.h> 16 + 17 + static DEFINE_SPINLOCK(sun9i_a80_cpus_lock); 18 + 19 + /** 20 + * sun9i_a80_cpus_clk_setup() - Setup function for a80 cpus composite clk 21 + */ 22 + 23 + #define SUN9I_CPUS_MAX_PARENTS 4 24 + #define SUN9I_CPUS_MUX_PARENT_PLL4 3 25 + #define SUN9I_CPUS_MUX_SHIFT 16 26 + #define SUN9I_CPUS_MUX_MASK GENMASK(17, 16) 27 + #define SUN9I_CPUS_MUX_GET_PARENT(reg) ((reg & SUN9I_CPUS_MUX_MASK) >> \ 28 + SUN9I_CPUS_MUX_SHIFT) 29 + 30 + #define SUN9I_CPUS_DIV_SHIFT 4 31 + #define SUN9I_CPUS_DIV_MASK GENMASK(5, 4) 32 + #define SUN9I_CPUS_DIV_GET(reg) ((reg & SUN9I_CPUS_DIV_MASK) >> \ 33 + SUN9I_CPUS_DIV_SHIFT) 34 + #define SUN9I_CPUS_DIV_SET(reg, div) ((reg & ~SUN9I_CPUS_DIV_MASK) | \ 35 + (div << SUN9I_CPUS_DIV_SHIFT)) 36 + #define SUN9I_CPUS_PLL4_DIV_SHIFT 8 37 + #define SUN9I_CPUS_PLL4_DIV_MASK GENMASK(12, 8) 38 + #define SUN9I_CPUS_PLL4_DIV_GET(reg) ((reg & SUN9I_CPUS_PLL4_DIV_MASK) >> \ 39 + SUN9I_CPUS_PLL4_DIV_SHIFT) 40 + #define SUN9I_CPUS_PLL4_DIV_SET(reg, div) ((reg & ~SUN9I_CPUS_PLL4_DIV_MASK) | \ 41 + (div << SUN9I_CPUS_PLL4_DIV_SHIFT)) 42 + 43 + struct sun9i_a80_cpus_clk { 44 + struct clk_hw hw; 45 + void __iomem *reg; 46 + }; 47 + 48 + #define to_sun9i_a80_cpus_clk(_hw) container_of(_hw, struct sun9i_a80_cpus_clk, hw) 49 + 50 + static unsigned long sun9i_a80_cpus_clk_recalc_rate(struct clk_hw *hw, 51 + unsigned long parent_rate) 52 + { 53 + struct sun9i_a80_cpus_clk *cpus = to_sun9i_a80_cpus_clk(hw); 54 + unsigned long rate; 55 + u32 reg; 56 + 57 + /* Fetch the register value */ 58 + reg = readl(cpus->reg); 59 + 60 + /* apply pre-divider first if parent is pll4 */ 61 + if (SUN9I_CPUS_MUX_GET_PARENT(reg) == SUN9I_CPUS_MUX_PARENT_PLL4) 62 + parent_rate /= SUN9I_CPUS_PLL4_DIV_GET(reg) + 1; 63 + 64 + /* clk divider */ 65 + rate = parent_rate / (SUN9I_CPUS_DIV_GET(reg) + 1); 66 + 67 + return rate; 68 + } 69 + 70 + static long sun9i_a80_cpus_clk_round(unsigned long rate, u8 *divp, u8 *pre_divp, 71 + u8 parent, unsigned long parent_rate) 72 + { 73 + u8 div, pre_div = 1; 74 + 75 + /* 76 + * clock can only divide, so we will never be able to achieve 77 + * frequencies higher than the parent frequency 78 + */ 79 + if (parent_rate && rate > parent_rate) 80 + rate = parent_rate; 81 + 82 + div = DIV_ROUND_UP(parent_rate, rate); 83 + 84 + /* calculate pre-divider if parent is pll4 */ 85 + if (parent == SUN9I_CPUS_MUX_PARENT_PLL4 && div > 4) { 86 + /* pre-divider is 1 ~ 32 */ 87 + if (div < 32) { 88 + pre_div = div; 89 + div = 1; 90 + } else if (div < 64) { 91 + pre_div = DIV_ROUND_UP(div, 2); 92 + div = 2; 93 + } else if (div < 96) { 94 + pre_div = DIV_ROUND_UP(div, 3); 95 + div = 3; 96 + } else { 97 + pre_div = DIV_ROUND_UP(div, 4); 98 + div = 4; 99 + } 100 + } 101 + 102 + /* we were asked to pass back divider values */ 103 + if (divp) { 104 + *divp = div - 1; 105 + *pre_divp = pre_div - 1; 106 + } 107 + 108 + return parent_rate / pre_div / div; 109 + } 110 + 111 + static int sun9i_a80_cpus_clk_determine_rate(struct clk_hw *clk, 112 + struct clk_rate_request *req) 113 + { 114 + struct clk_hw *parent, *best_parent = NULL; 115 + int i, num_parents; 116 + unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; 117 + unsigned long rate = req->rate; 118 + 119 + /* find the parent that can help provide the fastest rate <= rate */ 120 + num_parents = clk_hw_get_num_parents(clk); 121 + for (i = 0; i < num_parents; i++) { 122 + parent = clk_hw_get_parent_by_index(clk, i); 123 + if (!parent) 124 + continue; 125 + if (clk_hw_get_flags(clk) & CLK_SET_RATE_PARENT) 126 + parent_rate = clk_hw_round_rate(parent, rate); 127 + else 128 + parent_rate = clk_hw_get_rate(parent); 129 + 130 + child_rate = sun9i_a80_cpus_clk_round(rate, NULL, NULL, i, 131 + parent_rate); 132 + 133 + if (child_rate <= rate && child_rate > best_child_rate) { 134 + best_parent = parent; 135 + best = parent_rate; 136 + best_child_rate = child_rate; 137 + } 138 + } 139 + 140 + if (!best_parent) 141 + return -EINVAL; 142 + 143 + req->best_parent_hw = best_parent; 144 + req->best_parent_rate = best; 145 + req->rate = best_child_rate; 146 + 147 + return 0; 148 + } 149 + 150 + static int sun9i_a80_cpus_clk_set_rate(struct clk_hw *hw, unsigned long rate, 151 + unsigned long parent_rate) 152 + { 153 + struct sun9i_a80_cpus_clk *cpus = to_sun9i_a80_cpus_clk(hw); 154 + unsigned long flags; 155 + u8 div, pre_div, parent; 156 + u32 reg; 157 + 158 + spin_lock_irqsave(&sun9i_a80_cpus_lock, flags); 159 + 160 + reg = readl(cpus->reg); 161 + 162 + /* need to know which parent is used to apply pre-divider */ 163 + parent = SUN9I_CPUS_MUX_GET_PARENT(reg); 164 + sun9i_a80_cpus_clk_round(rate, &div, &pre_div, parent, parent_rate); 165 + 166 + reg = SUN9I_CPUS_DIV_SET(reg, div); 167 + reg = SUN9I_CPUS_PLL4_DIV_SET(reg, pre_div); 168 + writel(reg, cpus->reg); 169 + 170 + spin_unlock_irqrestore(&sun9i_a80_cpus_lock, flags); 171 + 172 + return 0; 173 + } 174 + 175 + static const struct clk_ops sun9i_a80_cpus_clk_ops = { 176 + .determine_rate = sun9i_a80_cpus_clk_determine_rate, 177 + .recalc_rate = sun9i_a80_cpus_clk_recalc_rate, 178 + .set_rate = sun9i_a80_cpus_clk_set_rate, 179 + }; 180 + 181 + static void sun9i_a80_cpus_setup(struct device_node *node) 182 + { 183 + const char *clk_name = node->name; 184 + const char *parents[SUN9I_CPUS_MAX_PARENTS]; 185 + struct resource res; 186 + struct sun9i_a80_cpus_clk *cpus; 187 + struct clk_mux *mux; 188 + struct clk *clk; 189 + int ret; 190 + 191 + cpus = kzalloc(sizeof(*cpus), GFP_KERNEL); 192 + if (!cpus) 193 + return; 194 + 195 + cpus->reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 196 + if (IS_ERR(cpus->reg)) 197 + goto err_free_cpus; 198 + 199 + of_property_read_string(node, "clock-output-names", &clk_name); 200 + 201 + /* we have a mux, we will have >1 parents */ 202 + ret = of_clk_parent_fill(node, parents, SUN9I_CPUS_MAX_PARENTS); 203 + 204 + mux = kzalloc(sizeof(*mux), GFP_KERNEL); 205 + if (!mux) 206 + goto err_unmap; 207 + 208 + /* set up clock properties */ 209 + mux->reg = cpus->reg; 210 + mux->shift = SUN9I_CPUS_MUX_SHIFT; 211 + /* un-shifted mask is what mux_clk expects */ 212 + mux->mask = SUN9I_CPUS_MUX_MASK >> SUN9I_CPUS_MUX_SHIFT; 213 + mux->lock = &sun9i_a80_cpus_lock; 214 + 215 + clk = clk_register_composite(NULL, clk_name, parents, ret, 216 + &mux->hw, &clk_mux_ops, 217 + &cpus->hw, &sun9i_a80_cpus_clk_ops, 218 + NULL, NULL, 0); 219 + if (IS_ERR(clk)) 220 + goto err_free_mux; 221 + 222 + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); 223 + if (ret) 224 + goto err_unregister; 225 + 226 + return; 227 + 228 + err_unregister: 229 + clk_unregister(clk); 230 + err_free_mux: 231 + kfree(mux); 232 + err_unmap: 233 + iounmap(cpus->reg); 234 + of_address_to_resource(node, 0, &res); 235 + release_mem_region(res.start, resource_size(&res)); 236 + err_free_cpus: 237 + kfree(cpus); 238 + } 239 + CLK_OF_DECLARE(sun9i_a80_cpus, "allwinner,sun9i-a80-cpus-clk", 240 + sun9i_a80_cpus_setup);
+6
drivers/clk/sunxi/clk-sunxi.c
··· 778 778 .shift = 12, 779 779 }; 780 780 781 + static const struct mux_data sun8i_h3_ahb2_mux_data __initconst = { 782 + .shift = 0, 783 + }; 784 + 781 785 static void __init sunxi_mux_clk_setup(struct device_node *node, 782 786 struct mux_data *data) 783 787 { ··· 1134 1130 static const struct of_device_id clk_mux_match[] __initconst = { 1135 1131 {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,}, 1136 1132 {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,}, 1133 + {.compatible = "allwinner,sun8i-h3-ahb2-clk", .data = &sun8i_h3_ahb2_mux_data,}, 1137 1134 {} 1138 1135 }; 1139 1136 ··· 1217 1212 CLK_OF_DECLARE(sun6i_a31s_clk_init, "allwinner,sun6i-a31s", sun6i_init_clocks); 1218 1213 CLK_OF_DECLARE(sun8i_a23_clk_init, "allwinner,sun8i-a23", sun6i_init_clocks); 1219 1214 CLK_OF_DECLARE(sun8i_a33_clk_init, "allwinner,sun8i-a33", sun6i_init_clocks); 1215 + CLK_OF_DECLARE(sun8i_h3_clk_init, "allwinner,sun8i-h3", sun6i_init_clocks); 1220 1216 1221 1217 static void __init sun9i_init_clocks(struct device_node *node) 1222 1218 {
+12
drivers/clk/sunxi/clk-usb.c
··· 243 243 sunxi_usb_clk_setup(node, &sun9i_a80_usb_phy_data, &a80_usb_phy_lock); 244 244 } 245 245 CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk", sun9i_a80_usb_phy_setup); 246 + 247 + static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = { 248 + .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) | 249 + BIT(11) | BIT(10) | BIT(9) | BIT(8), 250 + .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), 251 + }; 252 + 253 + static void __init sun8i_h3_usb_setup(struct device_node *node) 254 + { 255 + sunxi_usb_clk_setup(node, &sun8i_h3_usb_clk_data, &sun4i_a10_usb_lock); 256 + } 257 + CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk", sun8i_h3_usb_setup);