summaryrefslogtreecommitdiffstats
path: root/arch/ppc/syslib/m8xx_wdt.c
blob: ac11d7bab443840c193a6b0c741a7ba31b3663b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * m8xx_wdt.c - MPC8xx watchdog driver
 *
 * Author: Florian Schirmer <jolt@tuxbox.org>
 *
 * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
 * the terms of the GNU General Public License version 2. This program
 * is licensed "as is" without any warranty of any kind, whether express
 * or implied.
 */

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/8xx_immap.h>
#include <syslib/m8xx_wdt.h>

static int wdt_timeout;
int m8xx_has_internal_rtc = 0;

static irqreturn_t m8xx_wdt_interrupt(int, void *, struct pt_regs *);
static struct irqaction m8xx_wdt_irqaction = {
	.handler = m8xx_wdt_interrupt,
	.name = "watchdog",
};

void m8xx_wdt_reset(void)
{
	volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;

	out_be16(&imap->im_siu_conf.sc_swsr, 0x556c);	/* write magic1 */
	out_be16(&imap->im_siu_conf.sc_swsr, 0xaa39);	/* write magic2 */
}

static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
{
	volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;

	m8xx_wdt_reset();

	setbits16(&imap->im_sit.sit_piscr, PISCR_PS);
	return IRQ_HANDLED;
}

#define SYPCR_SWP 0x1
#define SYPCR_SWE 0x4


void __init m8xx_wdt_install_irq(volatile immap_t *imap, bd_t *binfo)
{
	u32 pitc;
	u32 pitrtclk;

	/*
	 * Fire trigger if half of the wdt ticked down 
	 */

	if (imap->im_sit.sit_rtcsc & RTCSC_38K)
		pitrtclk = 9600;
	else
		pitrtclk = 8192;

	if ((wdt_timeout) > (UINT_MAX / pitrtclk))
		pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2;
	else
		pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2;

	out_be32(&imap->im_sit.sit_pitc, pitc << 16);

	out_be16(&imap->im_sit.sit_piscr, (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE);

	if (setup_irq(PIT_INTERRUPT, &m8xx_wdt_irqaction))
		panic("m8xx_wdt: error setting up the watchdog irq!");

	printk(KERN_NOTICE
	       "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc);

}

static void m8xx_wdt_timer_func(unsigned long data);

static struct timer_list m8xx_wdt_timer =
	TIMER_INITIALIZER(m8xx_wdt_timer_func, 0, 0);

void m8xx_wdt_stop_timer(void)
{
	del_timer(&m8xx_wdt_timer);
}

void m8xx_wdt_install_timer(void)
{
	m8xx_wdt_timer.expires = jiffies + (HZ/2);
	add_timer(&m8xx_wdt_timer);
}

static void m8xx_wdt_timer_func(unsigned long data)
{
	m8xx_wdt_reset();
	m8xx_wdt_install_timer();
}

void __init m8xx_wdt_handler_install(bd_t * binfo)
{
	volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
	u32 sypcr;

	sypcr = in_be32(&imap->im_siu_conf.sc_sypcr);

	if (!(sypcr & SYPCR_SWE)) {
		printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
		       sypcr);
		return;
	}

	m8xx_wdt_reset();

	printk(KERN_NOTICE
	       "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n",
	       (sypcr >> 16), sypcr & SYPCR_SWP);

	wdt_timeout = (sypcr >> 16) & 0xFFFF;

	if (!wdt_timeout)
		wdt_timeout = 0xFFFF;

	if (sypcr & SYPCR_SWP)
		wdt_timeout *= 2048;

	m8xx_has_internal_rtc = in_be16(&imap->im_sit.sit_rtcsc) & RTCSC_RTE;

	/* if the internal RTC is off use a kernel timer */
	if (!m8xx_has_internal_rtc) {
		if (wdt_timeout < (binfo->bi_intfreq/HZ))
			printk(KERN_ERR "m8xx_wdt: timeout too short for ktimer!\n");
		m8xx_wdt_install_timer();
	} else
		m8xx_wdt_install_irq(imap, binfo);

	wdt_timeout /= binfo->bi_intfreq;
}

int m8xx_wdt_get_timeout(void)
{
	return wdt_timeout;
}