Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Marvell MVEBU clock gating control. |
| 3 | * |
| 4 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> |
| 5 | * Andrew Lunn <andrew@lunn.ch> |
| 6 | * |
| 7 | * This file is licensed under the terms of the GNU General Public |
| 8 | * License version 2. This program is licensed "as is" without any |
| 9 | * warranty of any kind, whether express or implied. |
| 10 | */ |
| 11 | #include <linux/kernel.h> |
| 12 | #include <linux/bitops.h> |
| 13 | #include <linux/io.h> |
| 14 | #include <linux/clk.h> |
| 15 | #include <linux/clkdev.h> |
| 16 | #include <linux/clk-provider.h> |
| 17 | #include <linux/clk/mvebu.h> |
| 18 | #include <linux/of.h> |
| 19 | #include <linux/of_address.h> |
| 20 | |
| 21 | struct mvebu_gating_ctrl { |
| 22 | spinlock_t lock; |
| 23 | struct clk **gates; |
| 24 | int num_gates; |
| 25 | }; |
| 26 | |
| 27 | struct mvebu_soc_descr { |
| 28 | const char *name; |
| 29 | const char *parent; |
| 30 | int bit_idx; |
| 31 | }; |
| 32 | |
| 33 | #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) |
| 34 | |
Joshua Coombs | 3810e63 | 2013-01-06 11:10:39 +0100 | [diff] [blame] | 35 | static struct clk *mvebu_clk_gating_get_src( |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 36 | struct of_phandle_args *clkspec, void *data) |
| 37 | { |
| 38 | struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; |
| 39 | int n; |
| 40 | |
| 41 | if (clkspec->args_count < 1) |
| 42 | return ERR_PTR(-EINVAL); |
| 43 | |
| 44 | for (n = 0; n < ctrl->num_gates; n++) { |
| 45 | struct clk_gate *gate = |
| 46 | to_clk_gate(__clk_get_hw(ctrl->gates[n])); |
| 47 | if (clkspec->args[0] == gate->bit_idx) |
| 48 | return ctrl->gates[n]; |
| 49 | } |
| 50 | return ERR_PTR(-ENODEV); |
| 51 | } |
| 52 | |
| 53 | static void __init mvebu_clk_gating_setup( |
| 54 | struct device_node *np, const struct mvebu_soc_descr *descr) |
| 55 | { |
| 56 | struct mvebu_gating_ctrl *ctrl; |
| 57 | struct clk *clk; |
| 58 | void __iomem *base; |
| 59 | const char *default_parent = NULL; |
| 60 | int n; |
| 61 | |
| 62 | base = of_iomap(np, 0); |
| 63 | |
| 64 | clk = of_clk_get(np, 0); |
| 65 | if (!IS_ERR(clk)) { |
| 66 | default_parent = __clk_get_name(clk); |
| 67 | clk_put(clk); |
| 68 | } |
| 69 | |
| 70 | ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); |
| 71 | if (WARN_ON(!ctrl)) |
| 72 | return; |
| 73 | |
| 74 | spin_lock_init(&ctrl->lock); |
| 75 | |
| 76 | /* |
| 77 | * Count, allocate, and register clock gates |
| 78 | */ |
| 79 | for (n = 0; descr[n].name;) |
| 80 | n++; |
| 81 | |
| 82 | ctrl->num_gates = n; |
| 83 | ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), |
| 84 | GFP_KERNEL); |
| 85 | if (WARN_ON(!ctrl->gates)) { |
| 86 | kfree(ctrl); |
| 87 | return; |
| 88 | } |
| 89 | |
| 90 | for (n = 0; n < ctrl->num_gates; n++) { |
Gregory CLEMENT | c4c34d6 | 2012-11-17 15:22:29 +0100 | [diff] [blame] | 91 | u8 flags = 0; |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 92 | const char *parent = |
| 93 | (descr[n].parent) ? descr[n].parent : default_parent; |
Gregory CLEMENT | c4c34d6 | 2012-11-17 15:22:29 +0100 | [diff] [blame] | 94 | |
| 95 | /* |
| 96 | * On Armada 370, the DDR clock is a special case: it |
| 97 | * isn't taken by any driver, but should anyway be |
| 98 | * kept enabled, so we mark it as IGNORE_UNUSED for |
| 99 | * now. |
| 100 | */ |
| 101 | if (!strcmp(descr[n].name, "ddr")) |
| 102 | flags |= CLK_IGNORE_UNUSED; |
| 103 | |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 104 | ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, |
Gregory CLEMENT | c4c34d6 | 2012-11-17 15:22:29 +0100 | [diff] [blame] | 105 | flags, base, descr[n].bit_idx, 0, &ctrl->lock); |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 106 | WARN_ON(IS_ERR(ctrl->gates[n])); |
| 107 | } |
| 108 | of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | * SoC specific clock gating control |
| 113 | */ |
| 114 | |
Gregory CLEMENT | c4c34d6 | 2012-11-17 15:22:29 +0100 | [diff] [blame] | 115 | #ifdef CONFIG_MACH_ARMADA_370 |
| 116 | static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { |
| 117 | { "audio", NULL, 0 }, |
| 118 | { "pex0_en", NULL, 1 }, |
| 119 | { "pex1_en", NULL, 2 }, |
| 120 | { "ge1", NULL, 3 }, |
| 121 | { "ge0", NULL, 4 }, |
| 122 | { "pex0", NULL, 5 }, |
| 123 | { "pex1", NULL, 9 }, |
| 124 | { "sata0", NULL, 15 }, |
| 125 | { "sdio", NULL, 17 }, |
| 126 | { "tdm", NULL, 25 }, |
| 127 | { "ddr", NULL, 28 }, |
| 128 | { "sata1", NULL, 30 }, |
| 129 | { } |
| 130 | }; |
| 131 | #endif |
| 132 | |
| 133 | #ifdef CONFIG_MACH_ARMADA_XP |
| 134 | static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { |
| 135 | { "audio", NULL, 0 }, |
| 136 | { "ge3", NULL, 1 }, |
| 137 | { "ge2", NULL, 2 }, |
| 138 | { "ge1", NULL, 3 }, |
| 139 | { "ge0", NULL, 4 }, |
| 140 | { "pex0", NULL, 5 }, |
| 141 | { "pex1", NULL, 6 }, |
| 142 | { "pex2", NULL, 7 }, |
| 143 | { "pex3", NULL, 8 }, |
| 144 | { "bp", NULL, 13 }, |
| 145 | { "sata0lnk", NULL, 14 }, |
| 146 | { "sata0", "sata0lnk", 15 }, |
| 147 | { "lcd", NULL, 16 }, |
| 148 | { "sdio", NULL, 17 }, |
| 149 | { "usb0", NULL, 18 }, |
| 150 | { "usb1", NULL, 19 }, |
| 151 | { "usb2", NULL, 20 }, |
| 152 | { "xor0", NULL, 22 }, |
| 153 | { "crypto", NULL, 23 }, |
| 154 | { "tdm", NULL, 25 }, |
| 155 | { "xor1", NULL, 28 }, |
| 156 | { "sata1lnk", NULL, 29 }, |
| 157 | { "sata1", "sata1lnk", 30 }, |
| 158 | { } |
| 159 | }; |
| 160 | #endif |
| 161 | |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 162 | #ifdef CONFIG_ARCH_DOVE |
| 163 | static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { |
| 164 | { "usb0", NULL, 0 }, |
| 165 | { "usb1", NULL, 1 }, |
| 166 | { "ge", "gephy", 2 }, |
| 167 | { "sata", NULL, 3 }, |
| 168 | { "pex0", NULL, 4 }, |
| 169 | { "pex1", NULL, 5 }, |
| 170 | { "sdio0", NULL, 8 }, |
| 171 | { "sdio1", NULL, 9 }, |
| 172 | { "nand", NULL, 10 }, |
| 173 | { "camera", NULL, 11 }, |
| 174 | { "i2s0", NULL, 12 }, |
| 175 | { "i2s1", NULL, 13 }, |
| 176 | { "crypto", NULL, 15 }, |
| 177 | { "ac97", NULL, 21 }, |
| 178 | { "pdma", NULL, 22 }, |
| 179 | { "xor0", NULL, 23 }, |
| 180 | { "xor1", NULL, 24 }, |
| 181 | { "gephy", NULL, 30 }, |
| 182 | { } |
| 183 | }; |
| 184 | #endif |
| 185 | |
| 186 | #ifdef CONFIG_ARCH_KIRKWOOD |
| 187 | static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { |
| 188 | { "ge0", NULL, 0 }, |
| 189 | { "pex0", NULL, 2 }, |
| 190 | { "usb0", NULL, 3 }, |
| 191 | { "sdio", NULL, 4 }, |
| 192 | { "tsu", NULL, 5 }, |
| 193 | { "runit", NULL, 7 }, |
| 194 | { "xor0", NULL, 8 }, |
| 195 | { "audio", NULL, 9 }, |
Andrew Lunn | 2a4bd9f | 2013-02-05 22:52:51 +0100 | [diff] [blame] | 196 | { "powersave", "cpuclk", 11 }, |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 197 | { "sata0", NULL, 14 }, |
| 198 | { "sata1", NULL, 15 }, |
| 199 | { "xor1", NULL, 16 }, |
| 200 | { "crypto", NULL, 17 }, |
| 201 | { "pex1", NULL, 18 }, |
| 202 | { "ge1", NULL, 19 }, |
| 203 | { "tdm", NULL, 20 }, |
| 204 | { } |
| 205 | }; |
| 206 | #endif |
| 207 | |
| 208 | static const __initdata struct of_device_id clk_gating_match[] = { |
Gregory CLEMENT | c4c34d6 | 2012-11-17 15:22:29 +0100 | [diff] [blame] | 209 | #ifdef CONFIG_MACH_ARMADA_370 |
| 210 | { |
| 211 | .compatible = "marvell,armada-370-gating-clock", |
| 212 | .data = armada_370_gating_descr, |
| 213 | }, |
| 214 | #endif |
| 215 | |
| 216 | #ifdef CONFIG_MACH_ARMADA_XP |
| 217 | { |
| 218 | .compatible = "marvell,armada-xp-gating-clock", |
| 219 | .data = armada_xp_gating_descr, |
| 220 | }, |
| 221 | #endif |
| 222 | |
Sebastian Hesselbarth | f97d0d7 | 2012-11-17 15:22:26 +0100 | [diff] [blame] | 223 | #ifdef CONFIG_ARCH_DOVE |
| 224 | { |
| 225 | .compatible = "marvell,dove-gating-clock", |
| 226 | .data = dove_gating_descr, |
| 227 | }, |
| 228 | #endif |
| 229 | |
| 230 | #ifdef CONFIG_ARCH_KIRKWOOD |
| 231 | { |
| 232 | .compatible = "marvell,kirkwood-gating-clock", |
| 233 | .data = kirkwood_gating_descr, |
| 234 | }, |
| 235 | #endif |
| 236 | |
| 237 | { } |
| 238 | }; |
| 239 | |
| 240 | void __init mvebu_gating_clk_init(void) |
| 241 | { |
| 242 | struct device_node *np; |
| 243 | |
| 244 | for_each_matching_node(np, clk_gating_match) { |
| 245 | const struct of_device_id *match = |
| 246 | of_match_node(clk_gating_match, np); |
| 247 | mvebu_clk_gating_setup(np, |
| 248 | (const struct mvebu_soc_descr *)match->data); |
| 249 | } |
| 250 | } |