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.
35 #include <syslinux/boot.h>
39 #include "kontron_wdt.h"
41 struct kempld_device_data pld
;
42 struct kempld_watchdog_data wdt
;
44 char default_label
[255];
46 /* Default Timeout is 60sec */
50 #define do_div(n,base) ({ \
52 __res = ((unsigned long) n) % (unsigned) base; \
53 n = ((unsigned long) n) / (unsigned) base; \
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
);}
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
;
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 */
141 uint8_t index_reg
= ioread8(pld
.io_index
);
142 if ((index_reg
== 0xff) && (ioread8(pld
.io_data
) == 0xff)) {
146 printf("Kempld structure found at 0x%X (data @ 0x%X)\n",pld
.io_base
,pld
.io_data
);
150 printf("No IO Found !\n");
154 static int kempld_wdt_probe_stages(struct kempld_watchdog_data
*wdt
)
156 struct kempld_device_data
*pld
= wdt
->pld
;
159 uint32_t timeout_mask
;
160 struct kempld_watchdog_stage
*stage
;
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
));
177 goto err_alloc_stages
;
180 stage
->timeout_mask
= ~timeout_mask
;
181 wdt
->stage
[i
] = stage
;
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
;
192 wdt
->stage
[i
] = NULL
;
198 kempld_release_mutex(pld
);
199 printf("Cannot allocate stages\n");
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');
212 static int kempld_wdt_setstageaction(struct kempld_watchdog_data
*wdt
,
213 struct kempld_watchdog_stage
*stage
,
216 struct kempld_device_data
*pld
= wdt
->pld
;
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
;
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
));
236 static int kempld_wdt_setstagetimeout(struct kempld_watchdog_data
*wdt
,
237 struct kempld_watchdog_stage
*stage
,
240 struct kempld_device_data
*pld
= wdt
->pld
;
243 uint64_t stage_timeout64
;
244 uint32_t stage_timeout
;
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
)
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
),
269 static int kempld_wdt_settimeout(struct kempld_watchdog_data
*wdt
)
272 int stage_pretimeout
;
274 if ((wdt
->timeout
<= 0) ||
275 (wdt
->pretimeout
< 0) ||
276 (wdt
->pretimeout
> wdt
->timeout
)) {
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
;
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
);
303 if (stage_pretimeout
!= 0) {
304 ret
= kempld_wdt_setstagetimeout(wdt
, wdt
->pretimeout_stage
,
310 ret
= kempld_wdt_setstageaction(wdt
, wdt
->timeout_stage
,
311 KEMPLD_WDT_ACTION_RESET
);
315 ret
= kempld_wdt_setstagetimeout(wdt
, wdt
->timeout_stage
,
326 static int kempld_wdt_start(struct kempld_watchdog_data
*wdt
)
328 struct kempld_device_data
*pld
= wdt
->pld
;
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
))
343 /* A regular configuration file looks like
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);
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
[]) {
364 openconsole(&dev_rawcon_r
, &dev_stdcon_w
);
366 detect_parameters(argc
,argv
);
369 /* probe how many usable stages we have */
370 if (kempld_wdt_probe_stages(&wdt
)) {
371 printf("Cannot Probe Stages\n");
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
);
383 ret
= kempld_wdt_settimeout(&wdt
);
385 printf("Unable to setup timeout !\n");
389 ret
= kempld_wdt_start(&wdt
);
391 printf("Unable to start watchdog !\n");
397 printf("Watchog armed ! Rebooting in %d seconds if no feed occurs !\n",wdt
.timeout
);
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
);