1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> |
5 | */ |
6 | |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/err.h> |
9 | #include <linux/io.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | |
13 | #define MHZ (1000 * 1000) |
14 | |
15 | #define BASE_CPU_SHIFT 1 |
16 | #define BASE_CPU_MASK 0x7F |
17 | |
18 | #define CPU_AHB_SHIFT 12 |
19 | #define CPU_AHB_MASK 0x07 |
20 | |
21 | #define FIXED_BASE_SHIFT 8 |
22 | #define FIXED_BASE_MASK 0x01 |
23 | |
24 | #define CLASSIC_BASE_SHIFT 16 |
25 | #define CLASSIC_BASE_MASK 0x1F |
26 | |
27 | #define CX_BASE_SHIFT 15 |
28 | #define CX_BASE_MASK 0x3F |
29 | |
30 | #define CX_UNKNOWN_SHIFT 21 |
31 | #define CX_UNKNOWN_MASK 0x03 |
32 | |
33 | struct nspire_clk_info { |
34 | u32 base_clock; |
35 | u16 base_cpu_ratio; |
36 | u16 base_ahb_ratio; |
37 | }; |
38 | |
39 | |
40 | #define (var, prop) (((var)>>prop##_SHIFT) & prop##_MASK) |
41 | static void nspire_clkinfo_cx(u32 val, struct nspire_clk_info *clk) |
42 | { |
43 | if (EXTRACT(val, FIXED_BASE)) |
44 | clk->base_clock = 48 * MHZ; |
45 | else |
46 | clk->base_clock = 6 * EXTRACT(val, CX_BASE) * MHZ; |
47 | |
48 | clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * EXTRACT(val, CX_UNKNOWN); |
49 | clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1); |
50 | } |
51 | |
52 | static void nspire_clkinfo_classic(u32 val, struct nspire_clk_info *clk) |
53 | { |
54 | if (EXTRACT(val, FIXED_BASE)) |
55 | clk->base_clock = 27 * MHZ; |
56 | else |
57 | clk->base_clock = (300 - 6 * EXTRACT(val, CLASSIC_BASE)) * MHZ; |
58 | |
59 | clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * 2; |
60 | clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1); |
61 | } |
62 | |
63 | static void __init nspire_ahbdiv_setup(struct device_node *node, |
64 | void (*get_clkinfo)(u32, struct nspire_clk_info *)) |
65 | { |
66 | u32 val; |
67 | void __iomem *io; |
68 | struct clk_hw *hw; |
69 | const char *clk_name = node->name; |
70 | const char *parent_name; |
71 | struct nspire_clk_info info; |
72 | |
73 | io = of_iomap(node, index: 0); |
74 | if (!io) |
75 | return; |
76 | val = readl(addr: io); |
77 | iounmap(addr: io); |
78 | |
79 | get_clkinfo(val, &info); |
80 | |
81 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &clk_name); |
82 | parent_name = of_clk_get_parent_name(np: node, index: 0); |
83 | |
84 | hw = clk_hw_register_fixed_factor(NULL, name: clk_name, parent_name, flags: 0, |
85 | mult: 1, div: info.base_ahb_ratio); |
86 | if (!IS_ERR(ptr: hw)) |
87 | of_clk_add_hw_provider(np: node, get: of_clk_hw_simple_get, data: hw); |
88 | } |
89 | |
90 | static void __init nspire_ahbdiv_setup_cx(struct device_node *node) |
91 | { |
92 | nspire_ahbdiv_setup(node, get_clkinfo: nspire_clkinfo_cx); |
93 | } |
94 | |
95 | static void __init nspire_ahbdiv_setup_classic(struct device_node *node) |
96 | { |
97 | nspire_ahbdiv_setup(node, get_clkinfo: nspire_clkinfo_classic); |
98 | } |
99 | |
100 | CLK_OF_DECLARE(nspire_ahbdiv_cx, "lsi,nspire-cx-ahb-divider" , |
101 | nspire_ahbdiv_setup_cx); |
102 | CLK_OF_DECLARE(nspire_ahbdiv_classic, "lsi,nspire-classic-ahb-divider" , |
103 | nspire_ahbdiv_setup_classic); |
104 | |
105 | static void __init nspire_clk_setup(struct device_node *node, |
106 | void (*get_clkinfo)(u32, struct nspire_clk_info *)) |
107 | { |
108 | u32 val; |
109 | void __iomem *io; |
110 | struct clk_hw *hw; |
111 | const char *clk_name = node->name; |
112 | struct nspire_clk_info info; |
113 | |
114 | io = of_iomap(node, index: 0); |
115 | if (!io) |
116 | return; |
117 | val = readl(addr: io); |
118 | iounmap(addr: io); |
119 | |
120 | get_clkinfo(val, &info); |
121 | |
122 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &clk_name); |
123 | |
124 | hw = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0, |
125 | info.base_clock); |
126 | if (!IS_ERR(ptr: hw)) |
127 | of_clk_add_hw_provider(np: node, get: of_clk_hw_simple_get, data: hw); |
128 | else |
129 | return; |
130 | |
131 | pr_info("TI-NSPIRE Base: %uMHz CPU: %uMHz AHB: %uMHz\n" , |
132 | info.base_clock / MHZ, |
133 | info.base_clock / info.base_cpu_ratio / MHZ, |
134 | info.base_clock / info.base_ahb_ratio / MHZ); |
135 | } |
136 | |
137 | static void __init nspire_clk_setup_cx(struct device_node *node) |
138 | { |
139 | nspire_clk_setup(node, get_clkinfo: nspire_clkinfo_cx); |
140 | } |
141 | |
142 | static void __init nspire_clk_setup_classic(struct device_node *node) |
143 | { |
144 | nspire_clk_setup(node, get_clkinfo: nspire_clkinfo_classic); |
145 | } |
146 | |
147 | CLK_OF_DECLARE(nspire_clk_cx, "lsi,nspire-cx-clock" , nspire_clk_setup_cx); |
148 | CLK_OF_DECLARE(nspire_clk_classic, "lsi,nspire-classic-clock" , |
149 | nspire_clk_setup_classic); |
150 | |