Commit | Line | Data |
---|---|---|
9c81b2cc TM |
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> | |
9c81b2cc | 9 | #include <linux/platform_device.h> |
c1d88411 | 10 | #include <linux/property.h> |
9c81b2cc TM |
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 | |
fc5d2a2f TM |
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) | |
9c81b2cc TM |
30 | |
31 | #define NPCM_USB1PHYCTL_OFFSET 0x140 | |
32 | #define NPCM_USB2PHYCTL_OFFSET 0x144 | |
fc5d2a2f | 33 | #define NPCM_USB3PHYCTL_OFFSET 0x148 |
9c81b2cc TM |
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 | ||
fc5d2a2f TM |
59 | #define NPCM_IPSRST4 0x74 |
60 | #define NPCM_IPSRST4_USBPHY3 BIT(25) | |
61 | #define NPCM_IPSRST4_USB_HOST2 BIT(31) | |
62 | ||
9c81b2cc TM |
63 | #define NPCM_RC_RESETS_PER_REG 32 |
64 | #define NPCM_MASK_RESETS GENMASK(4, 0) | |
65 | ||
fc5d2a2f TM |
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 | ||
9c81b2cc TM |
86 | struct npcm_rc_data { |
87 | struct reset_controller_dev rcdev; | |
88 | struct notifier_block restart_nb; | |
fc5d2a2f TM |
89 | const struct npcm_reset_info *info; |
90 | struct regmap *gcr_regmap; | |
9c81b2cc TM |
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, 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(rc->base + ctrl_offset); | |
123 | if (set) | |
124 | writel(stat | rst_bit, rc->base + ctrl_offset); | |
125 | else | |
126 | writel(stat & ~rst_bit, rc->base + ctrl_offset); | |
127 | spin_unlock_irqrestore(&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, 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, 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(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 | { | |
fc5d2a2f | 156 | struct npcm_rc_data *rc = to_rc_data(rcdev); |
9c81b2cc | 157 | unsigned int offset, bit; |
fc5d2a2f TM |
158 | bool offset_found = false; |
159 | int off_num; | |
9c81b2cc TM |
160 | |
161 | offset = reset_spec->args[0]; | |
fc5d2a2f TM |
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) { | |
9c81b2cc TM |
170 | dev_err(rcdev->dev, "Error reset register (0x%x)\n", offset); |
171 | return -EINVAL; | |
172 | } | |
fc5d2a2f | 173 | |
9c81b2cc TM |
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[] = { | |
fc5d2a2f TM |
184 | { .compatible = "nuvoton,npcm750-reset", .data = &npxm7xx_reset_info}, |
185 | { .compatible = "nuvoton,npcm845-reset", .data = &npxm8xx_reset_info}, | |
9c81b2cc TM |
186 | { } |
187 | }; | |
188 | ||
fc5d2a2f | 189 | static void npcm_usb_reset_npcm7xx(struct npcm_rc_data *rc) |
9c81b2cc TM |
190 | { |
191 | u32 mdlr, iprst1, iprst2, iprst3; | |
9c81b2cc TM |
192 | u32 ipsrst1_bits = 0; |
193 | u32 ipsrst2_bits = NPCM_IPSRST2_USB_HOST; | |
194 | u32 ipsrst3_bits = 0; | |
9c81b2cc TM |
195 | |
196 | /* checking which USB device is enabled */ | |
fc5d2a2f TM |
197 | regmap_read(rc->gcr_regmap, NPCM_MDLR_OFFSET, &mdlr); |
198 | if (!(mdlr & NPCM7XX_MDLR_USBD0)) | |
9c81b2cc | 199 | ipsrst3_bits |= NPCM_IPSRST3_USBD0; |
fc5d2a2f | 200 | if (!(mdlr & NPCM7XX_MDLR_USBD1)) |
9c81b2cc | 201 | ipsrst1_bits |= NPCM_IPSRST1_USBD1; |
fc5d2a2f | 202 | if (!(mdlr & NPCM7XX_MDLR_USBD2_4)) |
9c81b2cc TM |
203 | ipsrst1_bits |= (NPCM_IPSRST1_USBD2 | |
204 | NPCM_IPSRST1_USBD3 | | |
205 | NPCM_IPSRST1_USBD4); | |
fc5d2a2f | 206 | if (!(mdlr & NPCM7XX_MDLR_USBD0)) { |
9c81b2cc TM |
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(rc->base + NPCM_IPSRST1); | |
216 | iprst2 = readl(rc->base + NPCM_IPSRST2); | |
217 | iprst3 = readl(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(iprst1, rc->base + NPCM_IPSRST1); | |
225 | writel(iprst2, rc->base + NPCM_IPSRST2); | |
226 | writel(iprst3, rc->base + NPCM_IPSRST3); | |
227 | ||
228 | /* clear USB PHY RS bit */ | |
fc5d2a2f | 229 | regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, |
9c81b2cc | 230 | NPCM_USBXPHYCTL_RS, 0); |
fc5d2a2f | 231 | regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, |
9c81b2cc TM |
232 | NPCM_USBXPHYCTL_RS, 0); |
233 | ||
234 | /* deassert reset USB PHY */ | |
235 | iprst3 &= ~(NPCM_IPSRST3_USBPHY1 | NPCM_IPSRST3_USBPHY2); | |
236 | writel(iprst3, rc->base + NPCM_IPSRST3); | |
237 | ||
238 | udelay(50); | |
239 | ||
240 | /* set USB PHY RS bit */ | |
fc5d2a2f | 241 | regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, |
9c81b2cc | 242 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); |
fc5d2a2f | 243 | regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, |
9c81b2cc TM |
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(iprst1, rc->base + NPCM_IPSRST1); | |
252 | writel(iprst2, rc->base + NPCM_IPSRST2); | |
253 | writel(iprst3, rc->base + NPCM_IPSRST3); | |
fc5d2a2f TM |
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(rc->gcr_regmap, NPCM_MDLR_OFFSET, &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(rc->base + NPCM_IPSRST1); | |
286 | iprst2 = readl(rc->base + NPCM_IPSRST2); | |
287 | iprst3 = readl(rc->base + NPCM_IPSRST3); | |
288 | iprst4 = readl(rc->base + NPCM_IPSRST4); | |
289 | ||
290 | iprst1 |= ipsrst1_bits; | |
291 | iprst2 |= ipsrst2_bits; | |
292 | iprst3 |= (ipsrst3_bits | NPCM_IPSRST3_USBPHY1 | | |
293 | NPCM_IPSRST3_USBPHY2); | |
ae358d71 | 294 | iprst4 |= ipsrst4_bits; |
fc5d2a2f TM |
295 | |
296 | writel(iprst1, rc->base + NPCM_IPSRST1); | |
297 | writel(iprst2, rc->base + NPCM_IPSRST2); | |
298 | writel(iprst3, rc->base + NPCM_IPSRST3); | |
299 | writel(iprst4, rc->base + NPCM_IPSRST4); | |
300 | ||
301 | /* clear USB PHY RS bit */ | |
302 | regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, | |
303 | NPCM_USBXPHYCTL_RS, 0); | |
304 | regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, | |
305 | NPCM_USBXPHYCTL_RS, 0); | |
306 | regmap_update_bits(rc->gcr_regmap, NPCM_USB3PHYCTL_OFFSET, | |
307 | NPCM_USBXPHYCTL_RS, 0); | |
308 | ||
309 | /* deassert reset USB PHY */ | |
310 | iprst3 &= ~(NPCM_IPSRST3_USBPHY1 | NPCM_IPSRST3_USBPHY2); | |
311 | writel(iprst3, rc->base + NPCM_IPSRST3); | |
312 | iprst4 &= ~NPCM_IPSRST4_USBPHY3; | |
313 | writel(iprst4, rc->base + NPCM_IPSRST4); | |
314 | ||
315 | /* set USB PHY RS bit */ | |
316 | regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET, | |
317 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); | |
318 | regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET, | |
319 | NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS); | |
320 | regmap_update_bits(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(iprst1, rc->base + NPCM_IPSRST1); | |
330 | writel(iprst2, rc->base + NPCM_IPSRST2); | |
331 | writel(iprst3, rc->base + NPCM_IPSRST3); | |
332 | writel(iprst4, 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(dev->of_node, "nuvoton,sysgcr"); | |
344 | if (IS_ERR(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("nuvoton,npcm750-gcr"); | |
348 | if (IS_ERR(rc->gcr_regmap)) { | |
349 | dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-gcr"); | |
350 | return PTR_ERR(rc->gcr_regmap); | |
351 | } | |
352 | } | |
353 | ||
c1d88411 | 354 | rc->info = device_get_match_data(dev); |
fc5d2a2f TM |
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 | } | |
9c81b2cc TM |
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(&pdev->dev, sizeof(*rc), GFP_KERNEL); | |
381 | if (!rc) | |
382 | return -ENOMEM; | |
383 | ||
384 | rc->base = devm_platform_ioremap_resource(pdev, 0); | |
385 | if (IS_ERR(rc->base)) | |
386 | return PTR_ERR(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 | ||
9c81b2cc TM |
396 | ret = devm_reset_controller_register(&pdev->dev, &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(pdev->dev.of_node, "nuvoton,sw-reset-number", | |
406 | &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); |