Commit 6e2ac20e9c47cf26a1dd5a0f05b93ef0afd3c1c5
Committed by
Wim Van Sebroeck
1 parent
6adb730dc2
watchdog: bcm281xx: Debugfs support
This change introduces debugfs support for the BCM281xx watchdog driver. Signed-off-by: Markus Mayer <markus.mayer@linaro.org> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Showing 3 changed files with 115 additions and 4 deletions Side-by-side Diff
arch/arm/configs/bcm_defconfig
drivers/watchdog/Kconfig
... | ... | @@ -1151,6 +1151,16 @@ |
1151 | 1151 | Say 'Y' or 'M' here to enable the driver. The module will be called |
1152 | 1152 | bcm_kona_wdt. |
1153 | 1153 | |
1154 | +config BCM_KONA_WDT_DEBUG | |
1155 | + bool "DEBUGFS support for BCM Kona Watchdog" | |
1156 | + depends on BCM_KONA_WDT | |
1157 | + help | |
1158 | + If enabled, adds /sys/kernel/debug/bcm_kona_wdt/info which provides | |
1159 | + access to the driver's internal data structures as well as watchdog | |
1160 | + timer hardware registres. | |
1161 | + | |
1162 | + If in doubt, say 'N'. | |
1163 | + | |
1154 | 1164 | config LANTIQ_WDT |
1155 | 1165 | tristate "Lantiq SoC watchdog" |
1156 | 1166 | depends on LANTIQ |
drivers/watchdog/bcm_kona_wdt.c
... | ... | @@ -11,6 +11,7 @@ |
11 | 11 | * GNU General Public License for more details. |
12 | 12 | */ |
13 | 13 | |
14 | +#include <linux/debugfs.h> | |
14 | 15 | #include <linux/delay.h> |
15 | 16 | #include <linux/err.h> |
16 | 17 | #include <linux/io.h> |
17 | 18 | |
... | ... | @@ -55,9 +56,13 @@ |
55 | 56 | */ |
56 | 57 | int resolution; |
57 | 58 | spinlock_t lock; |
59 | +#ifdef CONFIG_BCM_KONA_WDT_DEBUG | |
60 | + unsigned long busy_count; | |
61 | + struct dentry *debugfs; | |
62 | +#endif | |
58 | 63 | }; |
59 | 64 | |
60 | -static int secure_register_read(void __iomem *addr) | |
65 | +static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset) | |
61 | 66 | { |
62 | 67 | uint32_t val; |
63 | 68 | unsigned count = 0; |
64 | 69 | |
... | ... | @@ -70,10 +75,16 @@ |
70 | 75 | do { |
71 | 76 | if (unlikely(count > 1)) |
72 | 77 | udelay(5); |
73 | - val = readl_relaxed(addr); | |
78 | + val = readl_relaxed(wdt->base + offset); | |
74 | 79 | count++; |
75 | 80 | } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY); |
76 | 81 | |
82 | +#ifdef CONFIG_BCM_KONA_WDT_DEBUG | |
83 | + /* Remember the maximum number iterations due to WD_LOAD_FLAG */ | |
84 | + if (count > wdt->busy_count) | |
85 | + wdt->busy_count = count; | |
86 | +#endif | |
87 | + | |
77 | 88 | /* This is the only place we return a negative value. */ |
78 | 89 | if (val & SECWDOG_WD_LOAD_FLAG) |
79 | 90 | return -ETIMEDOUT; |
... | ... | @@ -84,6 +95,93 @@ |
84 | 95 | return val; |
85 | 96 | } |
86 | 97 | |
98 | +#ifdef CONFIG_BCM_KONA_WDT_DEBUG | |
99 | + | |
100 | +static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data) | |
101 | +{ | |
102 | + int ctl_val, cur_val, ret; | |
103 | + unsigned long flags; | |
104 | + struct bcm_kona_wdt *wdt = s->private; | |
105 | + | |
106 | + if (!wdt) | |
107 | + return seq_puts(s, "No device pointer\n"); | |
108 | + | |
109 | + spin_lock_irqsave(&wdt->lock, flags); | |
110 | + ctl_val = secure_register_read(wdt, SECWDOG_CTRL_REG); | |
111 | + cur_val = secure_register_read(wdt, SECWDOG_COUNT_REG); | |
112 | + spin_unlock_irqrestore(&wdt->lock, flags); | |
113 | + | |
114 | + if (ctl_val < 0 || cur_val < 0) { | |
115 | + ret = seq_puts(s, "Error accessing hardware\n"); | |
116 | + } else { | |
117 | + int ctl, cur, ctl_sec, cur_sec, res; | |
118 | + | |
119 | + ctl = ctl_val & SECWDOG_COUNT_MASK; | |
120 | + res = (ctl_val & SECWDOG_RES_MASK) >> SECWDOG_CLKS_SHIFT; | |
121 | + cur = cur_val & SECWDOG_COUNT_MASK; | |
122 | + ctl_sec = TICKS_TO_SECS(ctl, wdt); | |
123 | + cur_sec = TICKS_TO_SECS(cur, wdt); | |
124 | + ret = seq_printf(s, "Resolution: %d / %d\n" | |
125 | + "Control: %d s / %d (%#x) ticks\n" | |
126 | + "Current: %d s / %d (%#x) ticks\n" | |
127 | + "Busy count: %lu\n", res, | |
128 | + wdt->resolution, ctl_sec, ctl, ctl, cur_sec, | |
129 | + cur, cur, wdt->busy_count); | |
130 | + } | |
131 | + | |
132 | + return ret; | |
133 | +} | |
134 | + | |
135 | +static int bcm_kona_dbg_open(struct inode *inode, struct file *file) | |
136 | +{ | |
137 | + return single_open(file, bcm_kona_wdt_dbg_show, inode->i_private); | |
138 | +} | |
139 | + | |
140 | +static const struct file_operations bcm_kona_dbg_operations = { | |
141 | + .open = bcm_kona_dbg_open, | |
142 | + .read = seq_read, | |
143 | + .llseek = seq_lseek, | |
144 | + .release = single_release, | |
145 | +}; | |
146 | + | |
147 | +static void bcm_kona_wdt_debug_init(struct platform_device *pdev) | |
148 | +{ | |
149 | + struct dentry *dir; | |
150 | + struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev); | |
151 | + | |
152 | + if (!wdt) | |
153 | + return; | |
154 | + | |
155 | + wdt->debugfs = NULL; | |
156 | + | |
157 | + dir = debugfs_create_dir(BCM_KONA_WDT_NAME, NULL); | |
158 | + if (IS_ERR_OR_NULL(dir)) | |
159 | + return; | |
160 | + | |
161 | + if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt, | |
162 | + &bcm_kona_dbg_operations)) | |
163 | + wdt->debugfs = dir; | |
164 | + else | |
165 | + debugfs_remove_recursive(dir); | |
166 | +} | |
167 | + | |
168 | +static void bcm_kona_wdt_debug_exit(struct platform_device *pdev) | |
169 | +{ | |
170 | + struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev); | |
171 | + | |
172 | + if (wdt && wdt->debugfs) { | |
173 | + debugfs_remove_recursive(wdt->debugfs); | |
174 | + wdt->debugfs = NULL; | |
175 | + } | |
176 | +} | |
177 | + | |
178 | +#else | |
179 | + | |
180 | +static void bcm_kona_wdt_debug_init(struct platform_device *pdev) {} | |
181 | +static void bcm_kona_wdt_debug_exit(struct platform_device *pdev) {} | |
182 | + | |
183 | +#endif /* CONFIG_BCM_KONA_WDT_DEBUG */ | |
184 | + | |
87 | 185 | static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt, |
88 | 186 | unsigned mask, unsigned newval) |
89 | 187 | { |
... | ... | @@ -93,7 +191,7 @@ |
93 | 191 | |
94 | 192 | spin_lock_irqsave(&wdt->lock, flags); |
95 | 193 | |
96 | - val = secure_register_read(wdt->base + SECWDOG_CTRL_REG); | |
194 | + val = secure_register_read(wdt, SECWDOG_CTRL_REG); | |
97 | 195 | if (val < 0) { |
98 | 196 | ret = val; |
99 | 197 | } else { |
... | ... | @@ -140,7 +238,7 @@ |
140 | 238 | unsigned long flags; |
141 | 239 | |
142 | 240 | spin_lock_irqsave(&wdt->lock, flags); |
143 | - val = secure_register_read(wdt->base + SECWDOG_COUNT_REG); | |
241 | + val = secure_register_read(wdt, SECWDOG_COUNT_REG); | |
144 | 242 | spin_unlock_irqrestore(&wdt->lock, flags); |
145 | 243 | |
146 | 244 | if (val < 0) |
... | ... | @@ -229,6 +327,7 @@ |
229 | 327 | return ret; |
230 | 328 | } |
231 | 329 | |
330 | + bcm_kona_wdt_debug_init(pdev); | |
232 | 331 | dev_dbg(dev, "Broadcom Kona Watchdog Timer"); |
233 | 332 | |
234 | 333 | return 0; |
... | ... | @@ -236,6 +335,7 @@ |
236 | 335 | |
237 | 336 | static int bcm_kona_wdt_remove(struct platform_device *pdev) |
238 | 337 | { |
338 | + bcm_kona_wdt_debug_exit(pdev); | |
239 | 339 | bcm_kona_wdt_shutdown(pdev); |
240 | 340 | watchdog_unregister_device(&bcm_kona_wdt_wdd); |
241 | 341 | dev_dbg(&pdev->dev, "Watchdog driver disabled"); |