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 
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 */
iowrite8(uint8_t val,uint16_t addr)58 void iowrite8(uint8_t val, uint16_t addr) { outb(val,addr); }
iowrite16(uint16_t val,uint16_t addr)59 void iowrite16(uint16_t val, uint16_t addr) { outw(val,addr); }
iowrite32(uint32_t val,uint16_t addr)60 void iowrite32(uint32_t val, uint16_t addr) { outl(val,addr);}
ioread8(uint16_t addr)61 uint8_t ioread8(uint16_t addr)   { return inb(addr);}
ioread16(uint16_t addr)62 uint16_t ioread16(uint16_t addr) { return inw(addr);}
ioread32(uint32_t 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  */
kempld_set_index(struct kempld_device_data * pld,uint8_t index)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 
kempld_read8(struct kempld_device_data * pld,uint8_t 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);
85 }
86 
87 
kempld_write8(struct kempld_device_data * pld,uint8_t index,uint8_t 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);
91 }
92 
93 
kempld_read16(struct kempld_device_data * pld,uint8_t index)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 
kempld_write16(struct kempld_device_data * pld,uint8_t index,uint16_t data)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 
kempld_read32(struct kempld_device_data * pld,uint8_t index)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 
kempld_write32(struct kempld_device_data * pld,uint8_t index,uint32_t data)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 
kempld_release_mutex(struct kempld_device_data * pld)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 
init_structure(void)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 
kempld_probe(void)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 
kempld_wdt_probe_stages(struct kempld_watchdog_data * wdt)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 
kempld_wdt_keepalive(struct kempld_watchdog_data * wdt)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 
kempld_wdt_setstageaction(struct kempld_watchdog_data * wdt,struct kempld_watchdog_stage * stage,int action)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 
kempld_wdt_setstagetimeout(struct kempld_watchdog_data * wdt,struct kempld_watchdog_stage * stage,int timeout)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 
kempld_wdt_settimeout(struct kempld_watchdog_data * wdt)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 
kempld_wdt_start(struct kempld_watchdog_data * wdt)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 */
detect_parameters(const int argc,const char * argv[])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 
main(int argc,const char * argv[])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