1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * RZ/A1 Core CPG Clocks |
4 | * |
5 | * Copyright (C) 2013 Ideas On Board SPRL |
6 | * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> |
7 | */ |
8 | |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/clk/renesas.h> |
11 | #include <linux/init.h> |
12 | #include <linux/io.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/of.h> |
15 | #include <linux/of_address.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #define CPG_FRQCR 0x10 |
19 | #define CPG_FRQCR2 0x14 |
20 | |
21 | #define PPR0 0xFCFE3200 |
22 | #define PIBC0 0xFCFE7000 |
23 | |
24 | #define MD_CLK(x) ((x >> 2) & 1) /* P0_2 */ |
25 | |
26 | /* ----------------------------------------------------------------------------- |
27 | * Initialization |
28 | */ |
29 | |
30 | static u16 __init rz_cpg_read_mode_pins(void) |
31 | { |
32 | void __iomem *ppr0, *pibc0; |
33 | u16 modes; |
34 | |
35 | ppr0 = ioremap(PPR0, size: 2); |
36 | pibc0 = ioremap(PIBC0, size: 2); |
37 | BUG_ON(!ppr0 || !pibc0); |
38 | iowrite16(4, pibc0); /* enable input buffer */ |
39 | modes = ioread16(ppr0); |
40 | iounmap(addr: ppr0); |
41 | iounmap(addr: pibc0); |
42 | |
43 | return modes; |
44 | } |
45 | |
46 | static struct clk * __init |
47 | rz_cpg_register_clock(struct device_node *np, void __iomem *base, |
48 | const char *name) |
49 | { |
50 | u32 val; |
51 | unsigned mult; |
52 | static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 }; |
53 | |
54 | if (strcmp(name, "pll" ) == 0) { |
55 | unsigned int cpg_mode = MD_CLK(rz_cpg_read_mode_pins()); |
56 | const char *parent_name = of_clk_get_parent_name(np, index: cpg_mode); |
57 | |
58 | mult = cpg_mode ? (32 / 4) : 30; |
59 | |
60 | return clk_register_fixed_factor(NULL, name, parent_name, flags: 0, mult, div: 1); |
61 | } |
62 | |
63 | /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */ |
64 | if (!base) |
65 | return ERR_PTR(error: -ENXIO); |
66 | |
67 | /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3) |
68 | * and the constraint that always g <= i. To get the rz platform started, |
69 | * let them run at fixed current speed and implement the details later. |
70 | */ |
71 | if (strcmp(name, "i" ) == 0) |
72 | val = (readl(addr: base + CPG_FRQCR) >> 8) & 3; |
73 | else if (strcmp(name, "g" ) == 0) |
74 | val = readl(addr: base + CPG_FRQCR2) & 3; |
75 | else |
76 | return ERR_PTR(error: -EINVAL); |
77 | |
78 | mult = frqcr_tab[val]; |
79 | return clk_register_fixed_factor(NULL, name, parent_name: "pll" , flags: 0, mult, div: 3); |
80 | } |
81 | |
82 | static void __init rz_cpg_clocks_init(struct device_node *np) |
83 | { |
84 | struct clk_onecell_data *data; |
85 | struct clk **clks; |
86 | void __iomem *base; |
87 | unsigned i; |
88 | int num_clks; |
89 | |
90 | num_clks = of_property_count_strings(np, propname: "clock-output-names" ); |
91 | if (WARN(num_clks <= 0, "can't count CPG clocks\n" )) |
92 | return; |
93 | |
94 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
95 | clks = kcalloc(n: num_clks, size: sizeof(*clks), GFP_KERNEL); |
96 | BUG_ON(!data || !clks); |
97 | |
98 | data->clks = clks; |
99 | data->clk_num = num_clks; |
100 | |
101 | base = of_iomap(node: np, index: 0); |
102 | |
103 | for (i = 0; i < num_clks; ++i) { |
104 | const char *name; |
105 | struct clk *clk; |
106 | |
107 | of_property_read_string_index(np, propname: "clock-output-names" , index: i, output: &name); |
108 | |
109 | clk = rz_cpg_register_clock(np, base, name); |
110 | if (IS_ERR(ptr: clk)) |
111 | pr_err("%s: failed to register %pOFn %s clock (%ld)\n" , |
112 | __func__, np, name, PTR_ERR(clk)); |
113 | else |
114 | data->clks[i] = clk; |
115 | } |
116 | |
117 | of_clk_add_provider(np, clk_src_get: of_clk_src_onecell_get, data); |
118 | |
119 | cpg_mstp_add_clk_domain(np); |
120 | } |
121 | CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks" , rz_cpg_clocks_init); |
122 | |