Home | History | Annotate | Download | only in modules
      1 /*
      2  *  kempld_wdt.c - Kontron PLD watchdog driver
      3  *
      4  *  Copyright (c) 2010 Kontron Embedded Modules GmbH
      5  *  Author: Michael Brunner <michael.brunner (at) kontron.com>
      6  *  Author: Erwan Velu <erwan.velu (at) 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 
     41 struct kempld_device_data  pld;
     42 struct kempld_watchdog_data wdt;
     43 uint8_t status;
     44 char 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 */
     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);}
     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  */
     73 void 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 
     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);
     85 }
     86 
     87 
     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);
     91 }
     92 
     93 
     94 uint16_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 
    100 void 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 
    106 uint32_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 
    111 void 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 
    117 static void kempld_release_mutex(struct kempld_device_data *pld)
    118 {
    119         iowrite8(pld->last_index | KEMPLD_MUTEX_KEY, pld->io_index);
    120 }
    121 
    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;
    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 
    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;
    145         }
    146 	printf("Kempld structure found at 0x%X (data @ 0x%X)\n",pld.io_base,pld.io_data);
    147 	return 0;
    148 
    149 err_empty_io:
    150 	printf("No IO Found !\n");
    151 	return ret;
    152 }
    153 
    154 static 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;
    197 err_alloc_stages:
    198 	kempld_release_mutex(pld);
    199 	printf("Cannot allocate stages\n");
    200 	return ret;
    201 }
    202 
    203 static 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 
    212 static 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 
    236 static 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 
    269 static 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;
    321 err_setstage:
    322 err_check_values:
    323         return ret;
    324 }
    325 
    326 static 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 */
    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));
    358 		}
    359 	}
    360 }
    361 
    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();
    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 
    403 booting:
    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 }
    415