1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * BCM63268 Timer Clock and Reset Controller Driver |
4 | * |
5 | * Copyright (C) 2023 Álvaro Fernández Rojas <noltari@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/container_of.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/device.h> |
12 | #include <linux/io.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/reset-controller.h> |
15 | #include <linux/spinlock.h> |
16 | |
17 | #include <dt-bindings/clock/bcm63268-clock.h> |
18 | |
19 | #define BCM63268_TIMER_RESET_SLEEP_MIN_US 10000 |
20 | #define BCM63268_TIMER_RESET_SLEEP_MAX_US 20000 |
21 | |
22 | struct bcm63268_tclkrst_hw { |
23 | void __iomem *regs; |
24 | spinlock_t lock; |
25 | |
26 | struct reset_controller_dev rcdev; |
27 | struct clk_hw_onecell_data data; |
28 | }; |
29 | |
30 | struct bcm63268_tclk_table_entry { |
31 | const char * const name; |
32 | u8 bit; |
33 | }; |
34 | |
35 | static const struct bcm63268_tclk_table_entry bcm63268_timer_clocks[] = { |
36 | { |
37 | .name = "ephy1" , |
38 | .bit = BCM63268_TCLK_EPHY1, |
39 | }, { |
40 | .name = "ephy2" , |
41 | .bit = BCM63268_TCLK_EPHY2, |
42 | }, { |
43 | .name = "ephy3" , |
44 | .bit = BCM63268_TCLK_EPHY3, |
45 | }, { |
46 | .name = "gphy1" , |
47 | .bit = BCM63268_TCLK_GPHY1, |
48 | }, { |
49 | .name = "dsl" , |
50 | .bit = BCM63268_TCLK_DSL, |
51 | }, { |
52 | .name = "wakeon_ephy" , |
53 | .bit = BCM63268_TCLK_WAKEON_EPHY, |
54 | }, { |
55 | .name = "wakeon_dsl" , |
56 | .bit = BCM63268_TCLK_WAKEON_DSL, |
57 | }, { |
58 | .name = "fap1_pll" , |
59 | .bit = BCM63268_TCLK_FAP1, |
60 | }, { |
61 | .name = "fap2_pll" , |
62 | .bit = BCM63268_TCLK_FAP2, |
63 | }, { |
64 | .name = "uto_50" , |
65 | .bit = BCM63268_TCLK_UTO_50, |
66 | }, { |
67 | .name = "uto_extin" , |
68 | .bit = BCM63268_TCLK_UTO_EXTIN, |
69 | }, { |
70 | .name = "usb_ref" , |
71 | .bit = BCM63268_TCLK_USB_REF, |
72 | }, { |
73 | /* sentinel */ |
74 | } |
75 | }; |
76 | |
77 | static inline struct bcm63268_tclkrst_hw * |
78 | to_bcm63268_timer_reset(struct reset_controller_dev *rcdev) |
79 | { |
80 | return container_of(rcdev, struct bcm63268_tclkrst_hw, rcdev); |
81 | } |
82 | |
83 | static int bcm63268_timer_reset_update(struct reset_controller_dev *rcdev, |
84 | unsigned long id, bool assert) |
85 | { |
86 | struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev); |
87 | unsigned long flags; |
88 | uint32_t val; |
89 | |
90 | spin_lock_irqsave(&reset->lock, flags); |
91 | val = __raw_readl(addr: reset->regs); |
92 | if (assert) |
93 | val &= ~BIT(id); |
94 | else |
95 | val |= BIT(id); |
96 | __raw_writel(val, addr: reset->regs); |
97 | spin_unlock_irqrestore(lock: &reset->lock, flags); |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static int bcm63268_timer_reset_assert(struct reset_controller_dev *rcdev, |
103 | unsigned long id) |
104 | { |
105 | return bcm63268_timer_reset_update(rcdev, id, assert: true); |
106 | } |
107 | |
108 | static int bcm63268_timer_reset_deassert(struct reset_controller_dev *rcdev, |
109 | unsigned long id) |
110 | { |
111 | return bcm63268_timer_reset_update(rcdev, id, assert: false); |
112 | } |
113 | |
114 | static int bcm63268_timer_reset_reset(struct reset_controller_dev *rcdev, |
115 | unsigned long id) |
116 | { |
117 | bcm63268_timer_reset_update(rcdev, id, assert: true); |
118 | usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US, |
119 | BCM63268_TIMER_RESET_SLEEP_MAX_US); |
120 | |
121 | bcm63268_timer_reset_update(rcdev, id, assert: false); |
122 | /* |
123 | * Ensure component is taken out reset state by sleeping also after |
124 | * deasserting the reset. Otherwise, the component may not be ready |
125 | * for operation. |
126 | */ |
127 | usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US, |
128 | BCM63268_TIMER_RESET_SLEEP_MAX_US); |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static int bcm63268_timer_reset_status(struct reset_controller_dev *rcdev, |
134 | unsigned long id) |
135 | { |
136 | struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev); |
137 | |
138 | return !(__raw_readl(addr: reset->regs) & BIT(id)); |
139 | } |
140 | |
141 | static const struct reset_control_ops bcm63268_timer_reset_ops = { |
142 | .assert = bcm63268_timer_reset_assert, |
143 | .deassert = bcm63268_timer_reset_deassert, |
144 | .reset = bcm63268_timer_reset_reset, |
145 | .status = bcm63268_timer_reset_status, |
146 | }; |
147 | |
148 | static int bcm63268_tclk_probe(struct platform_device *pdev) |
149 | { |
150 | struct device *dev = &pdev->dev; |
151 | const struct bcm63268_tclk_table_entry *entry; |
152 | struct bcm63268_tclkrst_hw *hw; |
153 | struct clk_hw *clk; |
154 | u8 maxbit = 0; |
155 | int i, ret; |
156 | |
157 | for (entry = bcm63268_timer_clocks; entry->name; entry++) |
158 | maxbit = max(maxbit, entry->bit); |
159 | maxbit++; |
160 | |
161 | hw = devm_kzalloc(dev: &pdev->dev, struct_size(hw, data.hws, maxbit), |
162 | GFP_KERNEL); |
163 | if (!hw) |
164 | return -ENOMEM; |
165 | |
166 | platform_set_drvdata(pdev, data: hw); |
167 | |
168 | spin_lock_init(&hw->lock); |
169 | |
170 | hw->data.num = maxbit; |
171 | for (i = 0; i < maxbit; i++) |
172 | hw->data.hws[i] = ERR_PTR(error: -ENODEV); |
173 | |
174 | hw->regs = devm_platform_ioremap_resource(pdev, index: 0); |
175 | if (IS_ERR(ptr: hw->regs)) |
176 | return PTR_ERR(ptr: hw->regs); |
177 | |
178 | for (entry = bcm63268_timer_clocks; entry->name; entry++) { |
179 | clk = devm_clk_hw_register_gate(dev, entry->name, NULL, 0, |
180 | hw->regs, entry->bit, |
181 | CLK_GATE_BIG_ENDIAN, |
182 | &hw->lock); |
183 | if (IS_ERR(ptr: clk)) |
184 | return PTR_ERR(ptr: clk); |
185 | |
186 | hw->data.hws[entry->bit] = clk; |
187 | } |
188 | |
189 | ret = devm_of_clk_add_hw_provider(dev, get: of_clk_hw_onecell_get, |
190 | data: &hw->data); |
191 | if (ret) |
192 | return ret; |
193 | |
194 | hw->rcdev.of_node = dev->of_node; |
195 | hw->rcdev.ops = &bcm63268_timer_reset_ops; |
196 | |
197 | ret = devm_reset_controller_register(dev, rcdev: &hw->rcdev); |
198 | if (ret) |
199 | dev_err(dev, "Failed to register reset controller\n" ); |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static const struct of_device_id bcm63268_tclk_dt_ids[] = { |
205 | { .compatible = "brcm,bcm63268-timer-clocks" }, |
206 | { /* sentinel */ } |
207 | }; |
208 | |
209 | static struct platform_driver bcm63268_tclk = { |
210 | .probe = bcm63268_tclk_probe, |
211 | .driver = { |
212 | .name = "bcm63268-timer-clock" , |
213 | .of_match_table = bcm63268_tclk_dt_ids, |
214 | }, |
215 | }; |
216 | builtin_platform_driver(bcm63268_tclk); |
217 | |