]>
Commit | Line | Data |
---|---|---|
017af7f7 CP |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Watchdog timer for PowerPC Book-E systems | |
4 | */ | |
5 | ||
6 | #include <div64.h> | |
7 | #include <dm.h> | |
8 | #include <wdt.h> | |
9 | #include <asm/processor.h> | |
10 | ||
11 | #define WDTP_MASK TCR_WP(0x3f) | |
12 | ||
13 | /* For the specified period, determine the number of seconds | |
14 | * corresponding to the reset time. There will be a watchdog | |
15 | * exception at approximately 3/5 of this time. | |
16 | * | |
17 | * The formula to calculate this is given by: | |
18 | * 2.5 * (2^(63-period+1)) / timebase_freq | |
19 | * | |
20 | * In order to simplify things, we assume that period is | |
21 | * at least 1. This will still result in a very long timeout. | |
22 | */ | |
23 | static unsigned long long period_to_sec(unsigned int period) | |
24 | { | |
25 | unsigned long long tmp = 1ULL << (64 - period); | |
26 | unsigned long tmp2 = get_tbclk(); | |
27 | ||
28 | /* tmp may be a very large number and we don't want to overflow, | |
29 | * so divide the timebase freq instead of multiplying tmp | |
30 | */ | |
31 | tmp2 = tmp2 / 5 * 2; | |
32 | ||
33 | do_div(tmp, tmp2); | |
34 | return tmp; | |
35 | } | |
36 | ||
37 | /* | |
38 | * This procedure will find the highest period which will give a timeout | |
39 | * greater than the one required. e.g. for a bus speed of 66666666 and | |
40 | * and a parameter of 2 secs, then this procedure will return a value of 38. | |
41 | */ | |
42 | static unsigned int sec_to_period(unsigned int secs) | |
43 | { | |
44 | unsigned int period; | |
45 | ||
46 | for (period = 63; period > 0; period--) { | |
47 | if (period_to_sec(period) >= secs) | |
48 | return period; | |
49 | } | |
50 | return 0; | |
51 | } | |
52 | ||
53 | static int booke_wdt_reset(struct udevice *dev) | |
54 | { | |
55 | mtspr(SPRN_TSR, TSR_ENW | TSR_WIS); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | static int booke_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) | |
61 | { | |
62 | u32 val; | |
63 | unsigned int timeout = DIV_ROUND_UP(timeout_ms, 1000); | |
64 | ||
65 | /* clear status before enabling watchdog */ | |
66 | booke_wdt_reset(dev); | |
67 | val = mfspr(SPRN_TCR); | |
68 | val &= ~WDTP_MASK; | |
69 | val |= (TCR_WIE | TCR_WRC(WRC_CHIP) | TCR_WP(sec_to_period(timeout))); | |
70 | ||
71 | mtspr(SPRN_TCR, val); | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static int booke_wdt_stop(struct udevice *dev) | |
77 | { | |
78 | u32 val; | |
79 | ||
80 | val = mfspr(SPRN_TCR); | |
81 | val &= ~(TCR_WIE | WDTP_MASK); | |
82 | mtspr(SPRN_TCR, val); | |
83 | ||
84 | /* clear status to make sure nothing is pending */ | |
85 | booke_wdt_reset(dev); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | static const struct wdt_ops booke_wdt_ops = { | |
91 | .start = booke_wdt_start, | |
92 | .stop = booke_wdt_stop, | |
93 | .reset = booke_wdt_reset, | |
94 | }; | |
95 | ||
96 | static const struct udevice_id booke_wdt_ids[] = { | |
97 | { .compatible = "fsl,booke-wdt" }, | |
98 | {} | |
99 | }; | |
100 | ||
101 | U_BOOT_DRIVER(booke_wdt) = { | |
102 | .name = "booke_wdt", | |
103 | .id = UCLASS_WDT, | |
104 | .of_match = booke_wdt_ids, | |
105 | .ops = &booke_wdt_ops, | |
106 | }; |