1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2019 Nuvoton Technology corporation. |
3 | |
4 | #include <linux/delay.h> |
5 | #include <linux/err.h> |
6 | #include <linux/io.h> |
7 | #include <linux/init.h> |
8 | #include <linux/of.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/property.h> |
11 | #include <linux/reboot.h> |
12 | #include <linux/reset-controller.h> |
13 | #include <linux/spinlock.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/of_address.h> |
17 | |
18 | /* NPCM7xx GCR registers */ |
19 | #define NPCM_MDLR_OFFSET 0x7C |
20 | #define NPCM7XX_MDLR_USBD0 BIT(9) |
21 | #define NPCM7XX_MDLR_USBD1 BIT(8) |
22 | #define NPCM7XX_MDLR_USBD2_4 BIT(21) |
23 | #define NPCM7XX_MDLR_USBD5_9 BIT(22) |
24 | |
25 | /* NPCM8xx MDLR bits */ |
26 | #define NPCM8XX_MDLR_USBD0_3 BIT(9) |
27 | #define NPCM8XX_MDLR_USBD4_7 BIT(22) |
28 | #define NPCM8XX_MDLR_USBD8 BIT(24) |
29 | #define NPCM8XX_MDLR_USBD9 BIT(21) |
30 | |
31 | #define NPCM_USB1PHYCTL_OFFSET 0x140 |
32 | #define NPCM_USB2PHYCTL_OFFSET 0x144 |
33 | #define NPCM_USB3PHYCTL_OFFSET 0x148 |
34 | #define NPCM_USBXPHYCTL_RS BIT(28) |
35 | |
36 | /* NPCM7xx Reset registers */ |
37 | #define NPCM_SWRSTR 0x14 |
38 | #define NPCM_SWRST BIT(2) |
39 | |
40 | #define NPCM_IPSRST1 0x20 |
41 | #define NPCM_IPSRST1_USBD1 BIT(5) |
42 | #define NPCM_IPSRST1_USBD2 BIT(8) |
43 | #define NPCM_IPSRST1_USBD3 BIT(25) |
44 | #define NPCM_IPSRST1_USBD4 BIT(22) |
45 | #define NPCM_IPSRST1_USBD5 BIT(23) |
46 | #define NPCM_IPSRST1_USBD6 BIT(24) |
47 | |
48 | #define NPCM_IPSRST2 0x24 |
49 | #define NPCM_IPSRST2_USB_HOST BIT(26) |
50 | |
51 | #define NPCM_IPSRST3 0x34 |
52 | #define NPCM_IPSRST3_USBD0 BIT(4) |
53 | #define NPCM_IPSRST3_USBD7 BIT(5) |
54 | #define NPCM_IPSRST3_USBD8 BIT(6) |
55 | #define NPCM_IPSRST3_USBD9 BIT(7) |
56 | #define NPCM_IPSRST3_USBPHY1 BIT(24) |
57 | #define NPCM_IPSRST3_USBPHY2 BIT(25) |
58 | |
59 | #define NPCM_IPSRST4 0x74 |
60 | #define NPCM_IPSRST4_USBPHY3 BIT(25) |
61 | #define NPCM_IPSRST4_USB_HOST2 BIT(31) |
62 | |
63 | #define NPCM_RC_RESETS_PER_REG 32 |
64 | #define NPCM_MASK_RESETS GENMASK(4, 0) |
65 | |
66 | enum { |
67 | BMC_NPCM7XX = 0, |
68 | BMC_NPCM8XX, |
69 | }; |
70 | |
71 | static const u32 npxm7xx_ipsrst[] = {NPCM_IPSRST1, NPCM_IPSRST2, NPCM_IPSRST3}; |
72 | static const u32 npxm8xx_ipsrst[] = {NPCM_IPSRST1, NPCM_IPSRST2, NPCM_IPSRST3, |
73 | NPCM_IPSRST4}; |
74 | |
75 | struct npcm_reset_info { |
76 | u32 bmc_id; |
77 | u32 num_ipsrst; |
78 | const u32 *ipsrst; |
79 | }; |
80 | |
81 | static const struct npcm_reset_info npxm7xx_reset_info[] = { |
82 | {.bmc_id = BMC_NPCM7XX, .num_ipsrst = 3, .ipsrst = npxm7xx_ipsrst}}; |
83 | static const struct npcm_reset_info npxm8xx_reset_info[] = { |
84 | {.bmc_id = BMC_NPCM8XX, .num_ipsrst = 4, .ipsrst = npxm8xx_ipsrst}}; |
85 | |
86 | struct npcm_rc_data { |
87 | struct reset_controller_dev rcdev; |
88 | struct notifier_block restart_nb; |
89 | const struct npcm_reset_info *info; |
90 | struct regmap *gcr_regmap; |
91 | u32 sw_reset_number; |
92 | void __iomem *base; |
93 | spinlock_t lock; |
94 | }; |
95 | |
96 | #define to_rc_data(p) container_of(p, struct npcm_rc_data, rcdev) |
97 | |
98 | static int npcm_rc_restart(struct notifier_block *nb, unsigned long mode, |
99 | void *cmd) |
100 | { |
101 | struct npcm_rc_data *rc = container_of(nb, struct npcm_rc_data, |
102 | restart_nb); |
103 | |
104 | writel(NPCM_SWRST << rc->sw_reset_number, addr: rc->base + NPCM_SWRSTR); |
105 | mdelay(1000); |
106 | |
107 | pr_emerg("%s: unable to restart system\n" , __func__); |
108 | |
109 | return NOTIFY_DONE; |
110 | } |
111 | |
112 | static int npcm_rc_setclear_reset(struct reset_controller_dev *rcdev, |
113 | unsigned long id, bool set) |
114 | { |
115 | struct npcm_rc_data *rc = to_rc_data(rcdev); |
116 | unsigned int rst_bit = BIT(id & NPCM_MASK_RESETS); |
117 | unsigned int ctrl_offset = id >> 8; |
118 | unsigned long flags; |
119 | u32 stat; |
120 | |
121 | spin_lock_irqsave(&rc->lock, flags); |
122 | stat = readl(addr: rc->base + ctrl_offset); |
123 | if (set) |
124 | writel(val: stat | rst_bit, addr: rc->base + ctrl_offset); |
125 | else |
126 | writel(val: stat & ~rst_bit, addr: rc->base + ctrl_offset); |
127 | spin_unlock_irqrestore(lock: &rc->lock, flags); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static int npcm_rc_assert(struct reset_controller_dev *rcdev, unsigned long id) |
133 | { |
134 | return npcm_rc_setclear_reset(rcdev, id, set: true); |
135 | } |
136 | |
137 | static int npcm_rc_deassert(struct reset_controller_dev *rcdev, |
138 | unsigned long id) |
139 | { |
140 | return npcm_rc_setclear_reset(rcdev, id, set: false); |
141 | } |
142 | |
143 | static int npcm_rc_status(struct reset_controller_dev *rcdev, |
144 | unsigned long id) |
145 | { |
146 | struct npcm_rc_data *rc = to_rc_data(rcdev); |
147 | unsigned int rst_bit = BIT(id & NPCM_MASK_RESETS); |
148 | unsigned int ctrl_offset = id >> 8; |
149 | |
150 | return (readl(addr: rc->base + ctrl_offset) & rst_bit); |
151 | } |
152 | |
153 | static int npcm_reset_xlate(struct reset_controller_dev *rcdev, |
154 | const struct of_phandle_args *reset_spec) |
155 | { |
156 | struct npcm_rc_data *rc = to_rc_data(rcdev); |
157 | unsigned int offset, bit; |
158 | bool offset_found = false; |
159 | int off_num; |
160 | |
161 | offset = reset_spec->args[0]; |
162 | for (off_num = 0 ; off_num < rc->info->num_ipsrst ; off_num++) { |
163 | if (offset == rc->info->ipsrst[off_num]) { |
164 | offset_found = true; |
165 | break; |
166 | } |
167 | } |
168 | |
169 | if (!offset_found) { |
170 | dev_err(rcdev->dev, "Error reset register (0x%x)\n" , offset); |
171 | return -EINVAL; |
172 | } |
173 | |
174 | bit = reset_spec->args[1]; |
175 | if (bit >= NPCM_RC_RESETS_PER_REG) { |
176 | dev_err(rcdev->dev, "Error reset number (%d)\n" , bit); |
177 | return -EINVAL; |
178 | } |
179 | |
180 | return (offset << 8) | bit; |
181 | } |
182 | |
183 | static const struct of_device_id npcm_rc_match[] = { |
184 | { .compatible = "nuvoton,npcm750-reset" , .data = &npxm7xx_reset_info}, |
185 | { .compatible = "nuvoton,npcm845-reset" , .data = &npxm8xx_reset_info}, |
186 | { } |
187 | }; |
188 | |
189 | static void npcm_usb_reset_npcm7xx(struct npcm_rc_data *rc) |
190 | { |
191 | u32 mdlr, iprst1, iprst2, iprst3; |
192 | u32 ipsrst1_bits = 0; |
193 | u32 ipsrst2_bits = NPCM_IPSRST2_USB_HOST; |
194 | u32 ipsrst3_bits = 0; |
195 | |
196 | /* checking which USB device is enabled */ |
197 | regmap_read(map: rc->gcr_regmap, NPCM_MDLR_OFFSET, val: &mdlr); |
198 | if (!(mdlr & NPCM7XX_MDLR_USBD0)) |
199 | ipsrst3_bits |= NPCM_IPSRST3_USBD0; |
200 | if (!(mdlr & NPCM7XX_MDLR_USBD1)) |
201 | ipsrst1_bits |= NPCM_IPSRST1_USBD1; |
202 | if (!(mdlr & NPCM7XX_MDLR_USBD2_4)) |
203 | ipsrst1_bits |= (NPCM_IPSRST1_USBD2 | |
204 | NPCM_IPSRST1_USBD3 | |
205 | NPCM_IPSRST1_USBD4); |
206 | if (!(mdlr & NPCM7XX_MDLR_USBD0)) { |
207 | ipsrst1_bits |= (NPCM_IPSRST1_USBD5 | |
208 | NPCM_IPSRST1_USBD6); |
209 | ipsrst3_bits |= (NPCM_IPSRST3_USBD7 | |
210 | NPCM_IPSRST3_USBD8 | |
211 | NPCM_IPSRST3_USBD9); |
212 | } |
213 | |
214 | /* assert reset USB PHY and USB devices */ |
215 | iprst1 = readl(addr: rc->base + NPCM_IPSRST1); |
216 | iprst2 = readl(addr: rc->base + NPCM_IPSRST2); |
217 | iprst3 = readl(addr: rc->base + NPCM_IPSRST3); |
218 | |
219 | iprst1 |= ipsrst1_bits; |
220 | iprst2 |= ipsrst2_bits; |
221 | iprst3 |= (ipsrst3_bits | NPCM_IPSRST3_USBPHY1 | |
222 | NPCM_IPSRST3_USBPHY2); |
223 | |
224 | writel(val: iprst1, addr: rc->base + NPCM_IPSRST1); |
225 | writel(val: iprst2, addr: rc->base + NPCM_IPSRST2); |
226 | writel(val: iprst3, addr: rc->base + NPCM_IPSRST3); |
227 | |
228 | /* clear USB PHY RS bit */ |
229 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, |
230 | NPCM_USBXPHYCTL_RS, val: 0); |
231 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, |
232 | NPCM_USBXPHYCTL_RS, val: 0); |
233 | |
234 | /* deassert reset USB PHY */ |
235 | iprst3 &= ~(NPCM_IPSRST3_USBPHY1 | NPCM_IPSRST3_USBPHY2); |
236 | writel(val: iprst3, addr: rc->base + NPCM_IPSRST3); |
237 | |
238 | udelay(50); |
239 | |
240 | /* set USB PHY RS bit */ |
241 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, |
242 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); |
243 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, |
244 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); |
245 | |
246 | /* deassert reset USB devices*/ |
247 | iprst1 &= ~ipsrst1_bits; |
248 | iprst2 &= ~ipsrst2_bits; |
249 | iprst3 &= ~ipsrst3_bits; |
250 | |
251 | writel(val: iprst1, addr: rc->base + NPCM_IPSRST1); |
252 | writel(val: iprst2, addr: rc->base + NPCM_IPSRST2); |
253 | writel(val: iprst3, addr: rc->base + NPCM_IPSRST3); |
254 | } |
255 | |
256 | static void npcm_usb_reset_npcm8xx(struct npcm_rc_data *rc) |
257 | { |
258 | u32 mdlr, iprst1, iprst2, iprst3, iprst4; |
259 | u32 ipsrst1_bits = 0; |
260 | u32 ipsrst2_bits = NPCM_IPSRST2_USB_HOST; |
261 | u32 ipsrst3_bits = 0; |
262 | u32 ipsrst4_bits = NPCM_IPSRST4_USB_HOST2 | NPCM_IPSRST4_USBPHY3; |
263 | |
264 | /* checking which USB device is enabled */ |
265 | regmap_read(map: rc->gcr_regmap, NPCM_MDLR_OFFSET, val: &mdlr); |
266 | if (!(mdlr & NPCM8XX_MDLR_USBD0_3)) { |
267 | ipsrst3_bits |= NPCM_IPSRST3_USBD0; |
268 | ipsrst1_bits |= (NPCM_IPSRST1_USBD1 | |
269 | NPCM_IPSRST1_USBD2 | |
270 | NPCM_IPSRST1_USBD3); |
271 | } |
272 | if (!(mdlr & NPCM8XX_MDLR_USBD4_7)) { |
273 | ipsrst1_bits |= (NPCM_IPSRST1_USBD4 | |
274 | NPCM_IPSRST1_USBD5 | |
275 | NPCM_IPSRST1_USBD6); |
276 | ipsrst3_bits |= NPCM_IPSRST3_USBD7; |
277 | } |
278 | |
279 | if (!(mdlr & NPCM8XX_MDLR_USBD8)) |
280 | ipsrst3_bits |= NPCM_IPSRST3_USBD8; |
281 | if (!(mdlr & NPCM8XX_MDLR_USBD9)) |
282 | ipsrst3_bits |= NPCM_IPSRST3_USBD9; |
283 | |
284 | /* assert reset USB PHY and USB devices */ |
285 | iprst1 = readl(addr: rc->base + NPCM_IPSRST1); |
286 | iprst2 = readl(addr: rc->base + NPCM_IPSRST2); |
287 | iprst3 = readl(addr: rc->base + NPCM_IPSRST3); |
288 | iprst4 = readl(addr: rc->base + NPCM_IPSRST4); |
289 | |
290 | iprst1 |= ipsrst1_bits; |
291 | iprst2 |= ipsrst2_bits; |
292 | iprst3 |= (ipsrst3_bits | NPCM_IPSRST3_USBPHY1 | |
293 | NPCM_IPSRST3_USBPHY2); |
294 | iprst4 |= ipsrst4_bits; |
295 | |
296 | writel(val: iprst1, addr: rc->base + NPCM_IPSRST1); |
297 | writel(val: iprst2, addr: rc->base + NPCM_IPSRST2); |
298 | writel(val: iprst3, addr: rc->base + NPCM_IPSRST3); |
299 | writel(val: iprst4, addr: rc->base + NPCM_IPSRST4); |
300 | |
301 | /* clear USB PHY RS bit */ |
302 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, |
303 | NPCM_USBXPHYCTL_RS, val: 0); |
304 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, |
305 | NPCM_USBXPHYCTL_RS, val: 0); |
306 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB3PHYCTL_OFFSET, |
307 | NPCM_USBXPHYCTL_RS, val: 0); |
308 | |
309 | /* deassert reset USB PHY */ |
310 | iprst3 &= ~(NPCM_IPSRST3_USBPHY1 | NPCM_IPSRST3_USBPHY2); |
311 | writel(val: iprst3, addr: rc->base + NPCM_IPSRST3); |
312 | iprst4 &= ~NPCM_IPSRST4_USBPHY3; |
313 | writel(val: iprst4, addr: rc->base + NPCM_IPSRST4); |
314 | |
315 | /* set USB PHY RS bit */ |
316 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, |
317 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); |
318 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, |
319 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); |
320 | regmap_update_bits(map: rc->gcr_regmap, NPCM_USB3PHYCTL_OFFSET, |
321 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); |
322 | |
323 | /* deassert reset USB devices*/ |
324 | iprst1 &= ~ipsrst1_bits; |
325 | iprst2 &= ~ipsrst2_bits; |
326 | iprst3 &= ~ipsrst3_bits; |
327 | iprst4 &= ~ipsrst4_bits; |
328 | |
329 | writel(val: iprst1, addr: rc->base + NPCM_IPSRST1); |
330 | writel(val: iprst2, addr: rc->base + NPCM_IPSRST2); |
331 | writel(val: iprst3, addr: rc->base + NPCM_IPSRST3); |
332 | writel(val: iprst4, addr: rc->base + NPCM_IPSRST4); |
333 | } |
334 | |
335 | /* |
336 | * The following procedure should be observed in USB PHY, USB device and |
337 | * USB host initialization at BMC boot |
338 | */ |
339 | static int npcm_usb_reset(struct platform_device *pdev, struct npcm_rc_data *rc) |
340 | { |
341 | struct device *dev = &pdev->dev; |
342 | |
343 | rc->gcr_regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node, property: "nuvoton,sysgcr" ); |
344 | if (IS_ERR(ptr: rc->gcr_regmap)) { |
345 | dev_warn(&pdev->dev, "Failed to find nuvoton,sysgcr property, please update the device tree\n" ); |
346 | dev_info(&pdev->dev, "Using nuvoton,npcm750-gcr for Poleg backward compatibility\n" ); |
347 | rc->gcr_regmap = syscon_regmap_lookup_by_compatible(s: "nuvoton,npcm750-gcr" ); |
348 | if (IS_ERR(ptr: rc->gcr_regmap)) { |
349 | dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-gcr" ); |
350 | return PTR_ERR(ptr: rc->gcr_regmap); |
351 | } |
352 | } |
353 | |
354 | rc->info = device_get_match_data(dev); |
355 | switch (rc->info->bmc_id) { |
356 | case BMC_NPCM7XX: |
357 | npcm_usb_reset_npcm7xx(rc); |
358 | break; |
359 | case BMC_NPCM8XX: |
360 | npcm_usb_reset_npcm8xx(rc); |
361 | break; |
362 | default: |
363 | return -ENODEV; |
364 | } |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | static const struct reset_control_ops npcm_rc_ops = { |
370 | .assert = npcm_rc_assert, |
371 | .deassert = npcm_rc_deassert, |
372 | .status = npcm_rc_status, |
373 | }; |
374 | |
375 | static int npcm_rc_probe(struct platform_device *pdev) |
376 | { |
377 | struct npcm_rc_data *rc; |
378 | int ret; |
379 | |
380 | rc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*rc), GFP_KERNEL); |
381 | if (!rc) |
382 | return -ENOMEM; |
383 | |
384 | rc->base = devm_platform_ioremap_resource(pdev, index: 0); |
385 | if (IS_ERR(ptr: rc->base)) |
386 | return PTR_ERR(ptr: rc->base); |
387 | |
388 | spin_lock_init(&rc->lock); |
389 | |
390 | rc->rcdev.owner = THIS_MODULE; |
391 | rc->rcdev.ops = &npcm_rc_ops; |
392 | rc->rcdev.of_node = pdev->dev.of_node; |
393 | rc->rcdev.of_reset_n_cells = 2; |
394 | rc->rcdev.of_xlate = npcm_reset_xlate; |
395 | |
396 | ret = devm_reset_controller_register(dev: &pdev->dev, rcdev: &rc->rcdev); |
397 | if (ret) { |
398 | dev_err(&pdev->dev, "unable to register device\n" ); |
399 | return ret; |
400 | } |
401 | |
402 | if (npcm_usb_reset(pdev, rc)) |
403 | dev_warn(&pdev->dev, "NPCM USB reset failed, can cause issues with UDC and USB host\n" ); |
404 | |
405 | if (!of_property_read_u32(np: pdev->dev.of_node, propname: "nuvoton,sw-reset-number" , |
406 | out_value: &rc->sw_reset_number)) { |
407 | if (rc->sw_reset_number && rc->sw_reset_number < 5) { |
408 | rc->restart_nb.priority = 192, |
409 | rc->restart_nb.notifier_call = npcm_rc_restart, |
410 | ret = register_restart_handler(&rc->restart_nb); |
411 | if (ret) |
412 | dev_warn(&pdev->dev, "failed to register restart handler\n" ); |
413 | } |
414 | } |
415 | |
416 | return ret; |
417 | } |
418 | |
419 | static struct platform_driver npcm_rc_driver = { |
420 | .probe = npcm_rc_probe, |
421 | .driver = { |
422 | .name = "npcm-reset" , |
423 | .of_match_table = npcm_rc_match, |
424 | .suppress_bind_attrs = true, |
425 | }, |
426 | }; |
427 | builtin_platform_driver(npcm_rc_driver); |
428 | |