kontron_wdt: Adjusting exit path
[hdt-cyring.git] / com32 / modules / kontron_wdt.c
blob6d21d7c20f81e62a4c06d5968703a26fe91fe3f6
1 /*
2 * kempld_wdt.c - Kontron PLD watchdog driver
4 * Copyright (c) 2010 Kontron Embedded Modules GmbH
5 * Author: Michael Brunner <michael.brunner@kontron.com>
6 * Author: Erwan Velu <erwan.velu@zodiacaerospace.com>
8 * Note: From the PLD watchdog point of view timeout and pretimeout are
9 * defined differently than in the kernel.
10 * First the pretimeout stage runs out before the timeout stage gets
11 * active. This has to be kept in mind.
13 * Kernel/API: P-----| pretimeout
14 * |-----------------------T timeout
15 * Watchdog: |-----------------P pretimeout_stage
16 * |-----T timeout_stage
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License 2 as published
20 * by the Free Software Foundation.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; see the file COPYING. If not, write to
29 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
32 #include <string.h>
33 #include <sys/io.h>
34 #include <unistd.h>
35 #include <syslinux/boot.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <console.h>
39 #include "kontron_wdt.h"
41 struct kempld_device_data pld;
42 struct kempld_watchdog_data wdt;
43 uint8_t status;
44 char default_label[255];
46 /* Default Timeout is 60sec */
47 #define TIMEOUT 60
48 #define PRETIMEOUT 0
50 #define do_div(n,base) ({ \
51 int __res; \
52 __res = ((unsigned long) n) % (unsigned) base; \
53 n = ((unsigned long) n) / (unsigned) base; \
54 __res; })
57 /* Basic Wrappers to get code as less changed as possible */
58 void iowrite8(uint8_t val, uint16_t addr) { outb(val,addr); }
59 void iowrite16(uint16_t val, uint16_t addr) { outw(val,addr); }
60 void iowrite32(uint32_t val, uint16_t addr) { outl(val,addr);}
61 uint8_t ioread8(uint16_t addr) { return inb(addr);}
62 uint16_t ioread16(uint16_t addr) { return inw(addr);}
63 uint32_t ioread32(uint32_t addr) { return inl(addr);}
66 /**
67 * kempld_set_index - change the current register index of the PLD
68 * @pld: kempld_device_data structure describing the PLD
69 * @index: register index on the chip
71 * This function changes the register index of the PLD.
73 void kempld_set_index(struct kempld_device_data *pld, uint8_t index)
75 if (pld->last_index != index) {
76 iowrite8(index, pld->io_index);
77 pld->last_index = index;
82 uint8_t kempld_read8(struct kempld_device_data *pld, uint8_t index) {
83 kempld_set_index(pld, index);
84 return ioread8(pld->io_data);
88 void kempld_write8(struct kempld_device_data *pld, uint8_t index, uint8_t data) {
89 kempld_set_index(pld, index);
90 iowrite8(data, pld->io_data);
94 uint16_t kempld_read16(struct kempld_device_data *pld, uint8_t index)
96 return kempld_read8(pld, index) | kempld_read8(pld, index+1) << 8;
100 void kempld_write16(struct kempld_device_data *pld, uint8_t index, uint16_t data)
102 kempld_write8(pld, index, (uint8_t)data);
103 kempld_write8(pld, index+1, (uint8_t)(data>>8));
106 uint32_t kempld_read32(struct kempld_device_data *pld, uint8_t index)
108 return kempld_read16(pld, index) | kempld_read16(pld, index+2) << 16;
111 void kempld_write32(struct kempld_device_data *pld, uint8_t index, uint32_t data)
113 kempld_write16(pld, index, (uint16_t)data);
114 kempld_write16(pld, index+2, (uint16_t)(data>>16));
117 static void kempld_release_mutex(struct kempld_device_data *pld)
119 iowrite8(pld->last_index | KEMPLD_MUTEX_KEY, pld->io_index);
122 void init_structure(void) {
123 /* set default values for the case we start the watchdog or change
124 * the configuration */
125 memset(&wdt,0,sizeof(wdt));
126 memset(&pld,0,sizeof(pld));
127 memset(&default_label,0,sizeof(default_label));
128 wdt.timeout = TIMEOUT;
129 wdt.pretimeout = PRETIMEOUT;
130 wdt.pld = &pld;
132 pld.io_base=KEMPLD_IOPORT;
133 pld.io_index=KEMPLD_IOPORT;
134 pld.io_data=KEMPLD_IODATA;
135 pld.pld_clock=33333333;
138 static int kempld_probe(void) {
139 /* Check for empty IO space */
140 int ret=0;
141 uint8_t index_reg = ioread8(pld.io_index);
142 if ((index_reg == 0xff) && (ioread8(pld.io_data) == 0xff)) {
143 ret = 1;
144 goto err_empty_io;
146 printf("Kempld structure found at 0x%X (data @ 0x%X)\n",pld.io_base,pld.io_data);
147 return 0;
149 err_empty_io:
150 printf("No IO Found !\n");
151 return ret;
154 static int kempld_wdt_probe_stages(struct kempld_watchdog_data *wdt)
156 struct kempld_device_data *pld = wdt->pld;
157 int i, ret;
158 uint32_t timeout;
159 uint32_t timeout_mask;
160 struct kempld_watchdog_stage *stage;
162 wdt->stages = 0;
163 wdt->timeout_stage = NULL;
164 wdt->pretimeout_stage = NULL;
166 for (i = 0; i < KEMPLD_WDT_MAX_STAGES; i++) {
168 timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i));
169 kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i), 0x00000000);
170 timeout_mask = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i));
171 kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(i), timeout);
173 if (timeout_mask != 0xffffffff) {
174 stage = malloc(sizeof(struct kempld_watchdog_stage));
175 if (stage == NULL) {
176 ret = -1;
177 goto err_alloc_stages;
179 stage->num = i;
180 stage->timeout_mask = ~timeout_mask;
181 wdt->stage[i] = stage;
182 wdt->stages++;
184 /* assign available stages to timeout and pretimeout */
185 if (wdt->stages == 1)
186 wdt->timeout_stage = stage;
187 else if (wdt->stages == 2) {
188 wdt->pretimeout_stage = wdt->timeout_stage;
189 wdt->timeout_stage = stage;
191 } else {
192 wdt->stage[i] = NULL;
196 return 0;
197 err_alloc_stages:
198 kempld_release_mutex(pld);
199 printf("Cannot allocate stages\n");
200 return ret;
203 static int kempld_wdt_keepalive(struct kempld_watchdog_data *wdt)
205 struct kempld_device_data *pld = wdt->pld;
207 kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
209 return 0;
212 static int kempld_wdt_setstageaction(struct kempld_watchdog_data *wdt,
213 struct kempld_watchdog_stage *stage,
214 int action)
216 struct kempld_device_data *pld = wdt->pld;
217 uint8_t stage_cfg;
219 if (stage == NULL)
220 return -1;
222 stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
223 stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ACTION_MASK;
224 stage_cfg |= (action & KEMPLD_WDT_STAGE_CFG_ACTION_MASK);
225 if (action == KEMPLD_WDT_ACTION_RESET)
226 stage_cfg |= KEMPLD_WDT_STAGE_CFG_ASSERT;
227 else
228 stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ASSERT;
230 kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
231 stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
233 return 0;
236 static int kempld_wdt_setstagetimeout(struct kempld_watchdog_data *wdt,
237 struct kempld_watchdog_stage *stage,
238 int timeout)
240 struct kempld_device_data *pld = wdt->pld;
241 uint8_t stage_cfg;
242 uint8_t prescaler;
243 uint64_t stage_timeout64;
244 uint32_t stage_timeout;
246 if (stage == NULL)
247 return -1;
249 prescaler = KEMPLD_WDT_PRESCALER_21BIT;
251 stage_timeout64 = ((uint64_t)timeout*pld->pld_clock);
252 do_div(stage_timeout64, KEMPLD_PRESCALER(prescaler));
253 stage_timeout = stage_timeout64 & stage->timeout_mask;
255 if (stage_timeout64 != (uint64_t)stage_timeout)
256 return -1;
258 stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
259 stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK;
260 stage_cfg |= KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(prescaler);
261 kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
262 kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->num),
263 stage_timeout);
265 return 0;
269 static int kempld_wdt_settimeout(struct kempld_watchdog_data *wdt)
271 int stage_timeout;
272 int stage_pretimeout;
273 int ret;
274 if ((wdt->timeout <= 0) ||
275 (wdt->pretimeout < 0) ||
276 (wdt->pretimeout > wdt->timeout)) {
277 ret = -1;
278 goto err_check_values;
281 if ((wdt->pretimeout == 0) || (wdt->pretimeout_stage == NULL)) {
282 if (wdt->pretimeout != 0)
283 printf("No pretimeout stage available, only enabling reset!\n");
284 stage_pretimeout = 0;
285 stage_timeout = wdt->timeout;
286 } else {
287 stage_pretimeout = wdt->timeout - wdt->pretimeout;
288 stage_timeout = wdt->pretimeout;
291 if (stage_pretimeout != 0) {
292 ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage,
293 KEMPLD_WDT_ACTION_NMI);
294 } else if ((stage_pretimeout == 0)
295 && (wdt->pretimeout_stage != NULL)) {
296 ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage,
297 KEMPLD_WDT_ACTION_NONE);
298 } else
299 ret = 0;
300 if (ret)
301 goto err_setstage;
303 if (stage_pretimeout != 0) {
304 ret = kempld_wdt_setstagetimeout(wdt, wdt->pretimeout_stage,
305 stage_pretimeout);
306 if (ret)
307 goto err_setstage;
310 ret = kempld_wdt_setstageaction(wdt, wdt->timeout_stage,
311 KEMPLD_WDT_ACTION_RESET);
312 if (ret)
313 goto err_setstage;
315 ret = kempld_wdt_setstagetimeout(wdt, wdt->timeout_stage,
316 stage_timeout);
317 if (ret)
318 goto err_setstage;
320 return 0;
321 err_setstage:
322 err_check_values:
323 return ret;
326 static int kempld_wdt_start(struct kempld_watchdog_data *wdt)
328 struct kempld_device_data *pld = wdt->pld;
329 uint8_t status;
331 status = kempld_read8(pld, KEMPLD_WDT_CFG);
332 status |= KEMPLD_WDT_CFG_ENABLE;
333 kempld_write8(pld, KEMPLD_WDT_CFG, status);
334 status = kempld_read8(pld, KEMPLD_WDT_CFG);
336 /* check if the watchdog was enabled */
337 if (!(status & KEMPLD_WDT_CFG_ENABLE))
338 return -1;
340 return 0;
343 /* A regular configuration file looks like
345 LABEL WDT
346 COM32 wdt.c32
347 APPEND timeout=120 default_label=local
349 void detect_parameters(const int argc, const char *argv[]) {
350 for (int i = 1; i < argc; i++) {
351 /* Override the timeout if specified on the cmdline */
352 if (!strncmp(argv[i], "timeout=", 8)) {
353 wdt.timeout=atoi(argv[i]+8);
354 } else
355 /* Define which boot entry shall be used */
356 if (!strncmp(argv[i], "default_label=", 14)) {
357 strlcpy(default_label, argv[i] + 14, sizeof(default_label));
362 int main(int argc, const char *argv[]) {
363 int ret=0;
364 openconsole(&dev_rawcon_r, &dev_stdcon_w);
365 init_structure();
366 detect_parameters(argc,argv);
367 kempld_probe();
369 /* probe how many usable stages we have */
370 if (kempld_wdt_probe_stages(&wdt)) {
371 printf("Cannot Probe Stages\n");
372 return -1;
375 /* Useless but who knows */
376 wdt.ident.firmware_version = KEMPLD_WDT_REV_GET(kempld_read8(&pld, KEMPLD_WDT_REV));
378 status = kempld_read8(&pld, KEMPLD_WDT_CFG);
379 /* kick the watchdog if it is already enabled, otherwise start it */
380 if (status & KEMPLD_WDT_CFG_ENABLE) {
381 kempld_wdt_keepalive(&wdt);
382 } else {
383 ret = kempld_wdt_settimeout(&wdt);
384 if (ret) {
385 printf("Unable to setup timeout !\n");
386 goto booting;
389 ret = kempld_wdt_start(&wdt);
390 if (ret) {
391 printf("Unable to start watchdog !\n");
392 goto booting;
397 printf("Watchog armed ! Rebooting in %d seconds if no feed occurs !\n",wdt.timeout);
399 booting:
400 /* Release Mutex to let Linux's Driver taking control */
401 kempld_release_mutex(&pld);
403 /* Let's boot the default entry if specified */
404 if (strlen(default_label)>0) {
405 printf("Executing default label = '%s'\n",default_label);
406 syslinux_run_command(default_label);
407 } else {
408 return ret;