diff options
author | Markus Mayer <markus.mayer@linaro.org> | 2014-01-06 16:56:10 -0500 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2014-01-28 15:17:53 -0500 |
commit | 6e2ac20e9c47cf26a1dd5a0f05b93ef0afd3c1c5 (patch) | |
tree | 05126e58c1dc75a751f7552934fb0561057344d3 | |
parent | 6adb730dc2085c16c52a2f991cc1661e4a7fd6d5 (diff) |
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>
-rw-r--r-- | arch/arm/configs/bcm_defconfig | 1 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 10 | ||||
-rw-r--r-- | drivers/watchdog/bcm_kona_wdt.c | 108 |
3 files changed, 115 insertions, 4 deletions
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig index fc3afd887a26..2519d6de0640 100644 --- a/arch/arm/configs/bcm_defconfig +++ b/arch/arm/configs/bcm_defconfig | |||
@@ -128,3 +128,4 @@ CONFIG_AVERAGE=y | |||
128 | CONFIG_PINCTRL_CAPRI=y | 128 | CONFIG_PINCTRL_CAPRI=y |
129 | CONFIG_WATCHDOG=y | 129 | CONFIG_WATCHDOG=y |
130 | CONFIG_BCM_KONA_WDT=y | 130 | CONFIG_BCM_KONA_WDT=y |
131 | CONFIG_BCM_KONA_WDT_DEBUG=y | ||
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 042e4521a8b8..1491f0f4d41d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -1151,6 +1151,16 @@ config BCM_KONA_WDT | |||
1151 | Say 'Y' or 'M' here to enable the driver. The module will be called | 1151 | Say 'Y' or 'M' here to enable the driver. The module will be called |
1152 | bcm_kona_wdt. | 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 | config LANTIQ_WDT | 1164 | config LANTIQ_WDT |
1155 | tristate "Lantiq SoC watchdog" | 1165 | tristate "Lantiq SoC watchdog" |
1156 | depends on LANTIQ | 1166 | depends on LANTIQ |
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index 7e41a83eb45e..9c248099f4a2 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c | |||
@@ -11,6 +11,7 @@ | |||
11 | * GNU General Public License for more details. | 11 | * GNU General Public License for more details. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/debugfs.h> | ||
14 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
15 | #include <linux/err.h> | 16 | #include <linux/err.h> |
16 | #include <linux/io.h> | 17 | #include <linux/io.h> |
@@ -55,9 +56,13 @@ struct bcm_kona_wdt { | |||
55 | */ | 56 | */ |
56 | int resolution; | 57 | int resolution; |
57 | spinlock_t lock; | 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 | uint32_t val; | 67 | uint32_t val; |
63 | unsigned count = 0; | 68 | unsigned count = 0; |
@@ -70,10 +75,16 @@ static int secure_register_read(void __iomem *addr) | |||
70 | do { | 75 | do { |
71 | if (unlikely(count > 1)) | 76 | if (unlikely(count > 1)) |
72 | udelay(5); | 77 | udelay(5); |
73 | val = readl_relaxed(addr); | 78 | val = readl_relaxed(wdt->base + offset); |
74 | count++; | 79 | count++; |
75 | } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY); | 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 | /* This is the only place we return a negative value. */ | 88 | /* This is the only place we return a negative value. */ |
78 | if (val & SECWDOG_WD_LOAD_FLAG) | 89 | if (val & SECWDOG_WD_LOAD_FLAG) |
79 | return -ETIMEDOUT; | 90 | return -ETIMEDOUT; |
@@ -84,6 +95,93 @@ static int secure_register_read(void __iomem *addr) | |||
84 | return val; | 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 | static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt, | 185 | static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt, |
88 | unsigned mask, unsigned newval) | 186 | unsigned mask, unsigned newval) |
89 | { | 187 | { |
@@ -93,7 +191,7 @@ static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt, | |||
93 | 191 | ||
94 | spin_lock_irqsave(&wdt->lock, flags); | 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 | if (val < 0) { | 195 | if (val < 0) { |
98 | ret = val; | 196 | ret = val; |
99 | } else { | 197 | } else { |
@@ -140,7 +238,7 @@ static unsigned int bcm_kona_wdt_get_timeleft(struct watchdog_device *wdog) | |||
140 | unsigned long flags; | 238 | unsigned long flags; |
141 | 239 | ||
142 | spin_lock_irqsave(&wdt->lock, flags); | 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 | spin_unlock_irqrestore(&wdt->lock, flags); | 242 | spin_unlock_irqrestore(&wdt->lock, flags); |
145 | 243 | ||
146 | if (val < 0) | 244 | if (val < 0) |
@@ -229,6 +327,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) | |||
229 | return ret; | 327 | return ret; |
230 | } | 328 | } |
231 | 329 | ||
330 | bcm_kona_wdt_debug_init(pdev); | ||
232 | dev_dbg(dev, "Broadcom Kona Watchdog Timer"); | 331 | dev_dbg(dev, "Broadcom Kona Watchdog Timer"); |
233 | 332 | ||
234 | return 0; | 333 | return 0; |
@@ -236,6 +335,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) | |||
236 | 335 | ||
237 | static int bcm_kona_wdt_remove(struct platform_device *pdev) | 336 | static int bcm_kona_wdt_remove(struct platform_device *pdev) |
238 | { | 337 | { |
338 | bcm_kona_wdt_debug_exit(pdev); | ||
239 | bcm_kona_wdt_shutdown(pdev); | 339 | bcm_kona_wdt_shutdown(pdev); |
240 | watchdog_unregister_device(&bcm_kona_wdt_wdd); | 340 | watchdog_unregister_device(&bcm_kona_wdt_wdd); |
241 | dev_dbg(&pdev->dev, "Watchdog driver disabled"); | 341 | dev_dbg(&pdev->dev, "Watchdog driver disabled"); |