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