source: bootcd/isolinux/syslinux-6.03/com32/modules/kontron_wdt.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 12.9 KB
Line 
1/*
2 *  kempld_wdt.c - Kontron PLD watchdog driver
3 *
4 *  Copyright (c) 2010 Kontron Embedded Modules GmbH
5 *  Author: Michael Brunner <michael.brunner@kontron.com>
6 *  Author: Erwan Velu <erwan.velu@zodiacaerospace.com>
7 *
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.
12 *
13 *  Kernel/API:                     P-----| pretimeout
14 *                |-----------------------T timeout
15 *  Watchdog:     |-----------------P       pretimeout_stage
16 *                                  |-----T timeout_stage
17 *
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.
21 *
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.
26 *
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.
30 */
31
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"
40
41struct kempld_device_data  pld;
42struct kempld_watchdog_data wdt;
43uint8_t status;
44char default_label[255];
45
46/* Default Timeout is 60sec */
47#define TIMEOUT 60
48#define PRETIMEOUT 0
49
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; })
55
56
57/* Basic Wrappers to get code as less changed as possible */
58void iowrite8(uint8_t val, uint16_t addr) { outb(val,addr); }
59void iowrite16(uint16_t val, uint16_t addr) { outw(val,addr); }
60void iowrite32(uint32_t val, uint16_t addr) { outl(val,addr);}
61uint8_t ioread8(uint16_t addr)   { return inb(addr);}
62uint16_t ioread16(uint16_t addr) { return inw(addr);}
63uint32_t ioread32(uint32_t addr) { return inl(addr);}
64
65
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
70 *
71 * This function changes the register index of the PLD.
72 */
73void kempld_set_index(struct kempld_device_data *pld, uint8_t index)
74{
75        if (pld->last_index != index) {
76                iowrite8(index, pld->io_index);
77                pld->last_index = index;
78        }
79}
80
81
82uint8_t kempld_read8(struct kempld_device_data *pld, uint8_t index) {
83        kempld_set_index(pld, index);
84        return ioread8(pld->io_data);
85}
86
87
88void 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);
91}
92
93
94uint16_t kempld_read16(struct kempld_device_data *pld, uint8_t index)
95{
96        return kempld_read8(pld, index) | kempld_read8(pld, index+1) << 8;
97}
98
99
100void kempld_write16(struct kempld_device_data *pld, uint8_t index, uint16_t data)
101{
102        kempld_write8(pld, index, (uint8_t)data);
103        kempld_write8(pld, index+1, (uint8_t)(data>>8));
104}
105
106uint32_t kempld_read32(struct kempld_device_data *pld, uint8_t index)
107{
108        return kempld_read16(pld, index) | kempld_read16(pld, index+2) << 16;
109}
110
111void kempld_write32(struct kempld_device_data *pld, uint8_t index, uint32_t data)
112{
113        kempld_write16(pld, index, (uint16_t)data);
114        kempld_write16(pld, index+2, (uint16_t)(data>>16));
115}
116
117static void kempld_release_mutex(struct kempld_device_data *pld)
118{
119        iowrite8(pld->last_index | KEMPLD_MUTEX_KEY, pld->io_index);
120}
121
122void 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;
131
132        pld.io_base=KEMPLD_IOPORT;
133        pld.io_index=KEMPLD_IOPORT;
134        pld.io_data=KEMPLD_IODATA;
135        pld.pld_clock=33333333;
136}
137
138static 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;
145        }
146        printf("Kempld structure found at 0x%X (data @ 0x%X)\n",pld.io_base,pld.io_data);
147        return 0;
148
149err_empty_io:
150        printf("No IO Found !\n");
151        return ret;
152}
153
154static int kempld_wdt_probe_stages(struct kempld_watchdog_data *wdt)
155{
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;
161
162        wdt->stages = 0;
163        wdt->timeout_stage = NULL;
164        wdt->pretimeout_stage = NULL;
165
166        for (i = 0; i < KEMPLD_WDT_MAX_STAGES; i++) {
167
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);
172
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;
178                        }
179                        stage->num = i;
180                        stage->timeout_mask = ~timeout_mask;
181                        wdt->stage[i] = stage;
182                        wdt->stages++;
183
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;
190                        }
191                } else {
192                        wdt->stage[i] = NULL;
193                }
194        }
195
196        return 0;
197err_alloc_stages:
198        kempld_release_mutex(pld);
199        printf("Cannot allocate stages\n");
200        return ret;
201}
202
203static int kempld_wdt_keepalive(struct kempld_watchdog_data *wdt)
204{
205        struct kempld_device_data *pld = wdt->pld;
206
207        kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
208
209        return 0;
210}
211
212static int kempld_wdt_setstageaction(struct kempld_watchdog_data *wdt,
213                                 struct kempld_watchdog_stage *stage,
214                                 int action)
215{
216        struct kempld_device_data *pld = wdt->pld;
217        uint8_t stage_cfg;
218
219        if (stage == NULL)
220                return -1;
221
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;
229
230        kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
231        stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
232
233        return 0;
234}
235
236static int kempld_wdt_setstagetimeout(struct kempld_watchdog_data *wdt,
237                                 struct kempld_watchdog_stage *stage,
238                                 int timeout)
239{
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;
245
246        if (stage == NULL)
247                return -1;
248
249        prescaler = KEMPLD_WDT_PRESCALER_21BIT;
250
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;
254
255        if (stage_timeout64 != (uint64_t)stage_timeout)
256                return -1;
257
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);
264
265        return 0;
266}
267
268
269static int kempld_wdt_settimeout(struct kempld_watchdog_data *wdt)
270{
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;
279        }
280
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;
289        }
290
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;
302
303        if (stage_pretimeout != 0) {
304                ret = kempld_wdt_setstagetimeout(wdt, wdt->pretimeout_stage,
305                                                 stage_pretimeout);
306                if (ret)
307                        goto err_setstage;
308        }
309
310        ret = kempld_wdt_setstageaction(wdt, wdt->timeout_stage,
311                                        KEMPLD_WDT_ACTION_RESET);
312        if (ret)
313                goto err_setstage;
314
315        ret = kempld_wdt_setstagetimeout(wdt, wdt->timeout_stage,
316                                         stage_timeout);
317        if (ret)
318                goto err_setstage;
319
320        return 0;
321err_setstage:
322err_check_values:
323        return ret;
324}
325
326static int kempld_wdt_start(struct kempld_watchdog_data *wdt)
327{
328        struct kempld_device_data *pld = wdt->pld;
329        uint8_t status;
330
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);
335
336        /* check if the watchdog was enabled */
337        if (!(status & KEMPLD_WDT_CFG_ENABLE))
338                return -1;
339
340        return 0;
341}
342
343/* A regular configuration file looks like
344
345   LABEL WDT
346       COM32 wdt.c32
347       APPEND timeout=120 default_label=local
348*/
349void 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));
358                }
359        }
360}
361
362int 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();
368
369        /* probe how many usable stages we have */
370        if (kempld_wdt_probe_stages(&wdt)) {
371                printf("Cannot Probe Stages\n");
372                return -1;
373        }
374
375        /* Useless but who knows */
376        wdt.ident.firmware_version = KEMPLD_WDT_REV_GET(kempld_read8(&pld, KEMPLD_WDT_REV));
377
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                /* Maybye the BIOS did setup a first timer
382                 * in this case, let's enforce the timeout
383                 * to be sure we do have the proper value */
384                kempld_wdt_settimeout(&wdt);
385                kempld_wdt_keepalive(&wdt);
386        } else {
387                ret = kempld_wdt_settimeout(&wdt);
388                if (ret) {
389                        printf("Unable to setup timeout !\n");
390                        goto booting;
391                }
392
393                ret = kempld_wdt_start(&wdt);
394                if (ret) {
395                        printf("Unable to start watchdog !\n");
396                        goto booting;
397                }
398
399        }
400
401        printf("Watchog armed ! Rebooting in %d seconds if no feed occurs !\n",wdt.timeout);
402
403booting:
404        /* Release Mutex to let Linux's Driver taking control */
405        kempld_release_mutex(&pld);
406
407        /* Let's boot the default entry if specified */
408        if (strlen(default_label)>0) {
409                printf("Executing default label = '%s'\n",default_label);
410                syslinux_run_command(default_label);
411        } else {
412                return ret;
413        }
414}
Note: See TracBrowser for help on using the repository browser.