Commit 825f0c26720668aefe3ed93be70e3bccf2337033

Authored by Dinh Nguyen
Committed by Olof Johansson
1 parent ea36b02269

ARM: socfpga: Add support to gate peripheral clocks

Add support to gate the clocks that directly feed peripherals. For clocks
with multiple parents, add the ability to determine the correct parent,
and also set parents. Also add support to calculate and set the clocks'
rate.

Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
Reviewed-by: Pavel Machek <pavel@denx.de>
Acked-by: Mike Turquette <mturquette@linaro.org>
Cc: Mike Turquette <mturquette@linaro.org>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Olof Johansson <olof@lixom.net>
Cc: Pavel Machek <pavel@denx.de>
CC: <linux@arm.linux.org.uk>

v4:
- Add Acked-by: Mike Turquette

v3:
- Addressed comments from Pavel

v2:
- Fix space/indent errors
- Add streq for strcmp == 0
Signed-off-by: Olof Johansson <olof@lixom.net>

Showing 1 changed file with 185 additions and 9 deletions Side-by-side Diff

drivers/clk/socfpga/clk.c
... ... @@ -24,15 +24,17 @@
24 24 #include <linux/of.h>
25 25  
26 26 /* Clock Manager offsets */
27   -#define CLKMGR_CTRL 0x0
28   -#define CLKMGR_BYPASS 0x4
  27 +#define CLKMGR_CTRL 0x0
  28 +#define CLKMGR_BYPASS 0x4
  29 +#define CLKMGR_L4SRC 0x70
  30 +#define CLKMGR_PERPLL_SRC 0xAC
29 31  
30 32 /* Clock bypass bits */
31   -#define MAINPLL_BYPASS (1<<0)
32   -#define SDRAMPLL_BYPASS (1<<1)
33   -#define SDRAMPLL_SRC_BYPASS (1<<2)
34   -#define PERPLL_BYPASS (1<<3)
35   -#define PERPLL_SRC_BYPASS (1<<4)
  33 +#define MAINPLL_BYPASS (1<<0)
  34 +#define SDRAMPLL_BYPASS (1<<1)
  35 +#define SDRAMPLL_SRC_BYPASS (1<<2)
  36 +#define PERPLL_BYPASS (1<<3)
  37 +#define PERPLL_SRC_BYPASS (1<<4)
36 38  
37 39 #define SOCFPGA_PLL_BG_PWRDWN 0
38 40 #define SOCFPGA_PLL_EXT_ENA 1
39 41  
... ... @@ -41,7 +43,18 @@
41 43 #define SOCFPGA_PLL_DIVF_SHIFT 3
42 44 #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
43 45 #define SOCFPGA_PLL_DIVQ_SHIFT 16
  46 +#define SOCFGPA_MAX_PARENTS 3
44 47  
  48 +#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
  49 +#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
  50 +#define SOCFPGA_NAND_CLK "nand_clk"
  51 +#define SOCFPGA_NAND_X_CLK "nand_x_clk"
  52 +#define SOCFPGA_MMC_CLK "mmc_clk"
  53 +#define SOCFPGA_DB_CLK "gpio_db_clk"
  54 +
  55 +#define div_mask(width) ((1 << (width)) - 1)
  56 +#define streq(a, b) (strcmp((a), (b)) == 0)
  57 +
45 58 extern void __iomem *clk_mgr_base_addr;
46 59  
47 60 struct socfpga_clk {
... ... @@ -49,6 +62,9 @@
49 62 char *parent_name;
50 63 char *clk_name;
51 64 u32 fixed_div;
  65 + void __iomem *div_reg;
  66 + u32 width; /* only valid if div_reg != 0 */
  67 + u32 shift; /* only valid if div_reg != 0 */
52 68 };
53 69 #define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
54 70  
... ... @@ -132,8 +148,9 @@
132 148  
133 149 socfpga_clk->hw.hw.init = &init;
134 150  
135   - if (strcmp(clk_name, "main_pll") || strcmp(clk_name, "periph_pll") ||
136   - strcmp(clk_name, "sdram_pll")) {
  151 + if (streq(clk_name, "main_pll") ||
  152 + streq(clk_name, "periph_pll") ||
  153 + streq(clk_name, "sdram_pll")) {
137 154 socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
138 155 clk_pll_ops.enable = clk_gate_ops.enable;
139 156 clk_pll_ops.disable = clk_gate_ops.disable;
... ... @@ -148,6 +165,159 @@
148 165 return clk;
149 166 }
150 167  
  168 +static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
  169 +{
  170 + u32 l4_src;
  171 + u32 perpll_src;
  172 +
  173 + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
  174 + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
  175 + return l4_src &= 0x1;
  176 + }
  177 + if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
  178 + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
  179 + return !!(l4_src & 2);
  180 + }
  181 +
  182 + perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
  183 + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
  184 + return perpll_src &= 0x3;
  185 + if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
  186 + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
  187 + return (perpll_src >> 2) & 3;
  188 +
  189 + /* QSPI clock */
  190 + return (perpll_src >> 4) & 3;
  191 +
  192 +}
  193 +
  194 +static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
  195 +{
  196 + u32 src_reg;
  197 +
  198 + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
  199 + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
  200 + src_reg &= ~0x1;
  201 + src_reg |= parent;
  202 + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
  203 + } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
  204 + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
  205 + src_reg &= ~0x2;
  206 + src_reg |= (parent << 1);
  207 + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
  208 + } else {
  209 + src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
  210 + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
  211 + src_reg &= ~0x3;
  212 + src_reg |= parent;
  213 + } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
  214 + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
  215 + src_reg &= ~0xC;
  216 + src_reg |= (parent << 2);
  217 + } else {/* QSPI clock */
  218 + src_reg &= ~0x30;
  219 + src_reg |= (parent << 4);
  220 + }
  221 + writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
  222 + }
  223 +
  224 + return 0;
  225 +}
  226 +
  227 +static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
  228 + unsigned long parent_rate)
  229 +{
  230 + struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
  231 + u32 div = 1, val;
  232 +
  233 + if (socfpgaclk->fixed_div)
  234 + div = socfpgaclk->fixed_div;
  235 + else if (socfpgaclk->div_reg) {
  236 + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
  237 + val &= div_mask(socfpgaclk->width);
  238 + if (streq(hwclk->init->name, SOCFPGA_DB_CLK))
  239 + div = val + 1;
  240 + else
  241 + div = (1 << val);
  242 + }
  243 +
  244 + return parent_rate / div;
  245 +}
  246 +
  247 +static struct clk_ops gateclk_ops = {
  248 + .recalc_rate = socfpga_clk_recalc_rate,
  249 + .get_parent = socfpga_clk_get_parent,
  250 + .set_parent = socfpga_clk_set_parent,
  251 +};
  252 +
  253 +static void __init socfpga_gate_clk_init(struct device_node *node,
  254 + const struct clk_ops *ops)
  255 +{
  256 + u32 clk_gate[2];
  257 + u32 div_reg[3];
  258 + u32 fixed_div;
  259 + struct clk *clk;
  260 + struct socfpga_clk *socfpga_clk;
  261 + const char *clk_name = node->name;
  262 + const char *parent_name[SOCFGPA_MAX_PARENTS];
  263 + struct clk_init_data init;
  264 + int rc;
  265 + int i = 0;
  266 +
  267 + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
  268 + if (WARN_ON(!socfpga_clk))
  269 + return;
  270 +
  271 + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
  272 + if (rc)
  273 + clk_gate[0] = 0;
  274 +
  275 + if (clk_gate[0]) {
  276 + socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
  277 + socfpga_clk->hw.bit_idx = clk_gate[1];
  278 +
  279 + gateclk_ops.enable = clk_gate_ops.enable;
  280 + gateclk_ops.disable = clk_gate_ops.disable;
  281 + }
  282 +
  283 + rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
  284 + if (rc)
  285 + socfpga_clk->fixed_div = 0;
  286 + else
  287 + socfpga_clk->fixed_div = fixed_div;
  288 +
  289 + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
  290 + if (!rc) {
  291 + socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
  292 + socfpga_clk->shift = div_reg[1];
  293 + socfpga_clk->width = div_reg[2];
  294 + } else {
  295 + socfpga_clk->div_reg = 0;
  296 + }
  297 +
  298 + of_property_read_string(node, "clock-output-names", &clk_name);
  299 +
  300 + init.name = clk_name;
  301 + init.ops = ops;
  302 + init.flags = 0;
  303 + while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
  304 + of_clk_get_parent_name(node, i)) != NULL)
  305 + i++;
  306 +
  307 + init.parent_names = parent_name;
  308 + init.num_parents = i;
  309 + socfpga_clk->hw.hw.init = &init;
  310 +
  311 + clk = clk_register(NULL, &socfpga_clk->hw.hw);
  312 + if (WARN_ON(IS_ERR(clk))) {
  313 + kfree(socfpga_clk);
  314 + return;
  315 + }
  316 + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
  317 + if (WARN_ON(rc))
  318 + return;
  319 +}
  320 +
151 321 static void __init socfpga_pll_init(struct device_node *node)
152 322 {
153 323 socfpga_clk_init(node, &clk_pll_ops);
... ... @@ -159,6 +329,12 @@
159 329 socfpga_clk_init(node, &periclk_ops);
160 330 }
161 331 CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init);
  332 +
  333 +static void __init socfpga_gate_init(struct device_node *node)
  334 +{
  335 + socfpga_gate_clk_init(node, &gateclk_ops);
  336 +}
  337 +CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init);
162 338  
163 339 void __init socfpga_init_clocks(void)
164 340 {