aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/watchdog/mv64x60_wdt.c90
1 files changed, 54 insertions, 36 deletions
diff --git a/drivers/char/watchdog/mv64x60_wdt.c b/drivers/char/watchdog/mv64x60_wdt.c
index 07582bb8aea4..0365c317f7e1 100644
--- a/drivers/char/watchdog/mv64x60_wdt.c
+++ b/drivers/char/watchdog/mv64x60_wdt.c
@@ -29,75 +29,95 @@
29 29
30#define MV64x60_WDT_WDC_OFFSET 0 30#define MV64x60_WDT_WDC_OFFSET 0
31 31
32/* MV64x60 WDC (config) register access definitions */ 32/*
33#define MV64x60_WDC_CTL1_MASK (3 << 24) 33 * The watchdog configuration register contains a pair of 2-bit fields,
34#define MV64x60_WDC_CTL1(val) ((val & 3) << 24) 34 * 1. a reload field, bits 27-26, which triggers a reload of
35#define MV64x60_WDC_CTL2_MASK (3 << 26) 35 * the countdown register, and
36#define MV64x60_WDC_CTL2(val) ((val & 3) << 26) 36 * 2. an enable field, bits 25-24, which toggles between
37 * enabling and disabling the watchdog timer.
38 * Bit 31 is a read-only field which indicates whether the
39 * watchdog timer is currently enabled.
40 *
41 * The low 24 bits contain the timer reload value.
42 */
43#define MV64x60_WDC_ENABLE_SHIFT 24
44#define MV64x60_WDC_SERVICE_SHIFT 26
45#define MV64x60_WDC_ENABLED_SHIFT 31
46
47#define MV64x60_WDC_ENABLED_TRUE 1
48#define MV64x60_WDC_ENABLED_FALSE 0
37 49
38/* Flags bits */ 50/* Flags bits */
39#define MV64x60_WDOG_FLAG_OPENED 0 51#define MV64x60_WDOG_FLAG_OPENED 0
40#define MV64x60_WDOG_FLAG_ENABLED 1
41 52
42static unsigned long wdt_flags; 53static unsigned long wdt_flags;
43static int wdt_status; 54static int wdt_status;
44static void __iomem *mv64x60_wdt_regs; 55static void __iomem *mv64x60_wdt_regs;
45static int mv64x60_wdt_timeout; 56static int mv64x60_wdt_timeout;
57static int mv64x60_wdt_count;
46static unsigned int bus_clk; 58static unsigned int bus_clk;
47static char expect_close; 59static char expect_close;
60static DEFINE_SPINLOCK(mv64x60_wdt_spinlock);
48 61
49static int nowayout = WATCHDOG_NOWAYOUT; 62static int nowayout = WATCHDOG_NOWAYOUT;
50module_param(nowayout, int, 0); 63module_param(nowayout, int, 0);
51MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 64MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
52 65
53static void mv64x60_wdt_reg_write(u32 val) 66static int mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift)
54{ 67{
55 /* Allow write only to CTL1 / CTL2 fields, retaining values in 68 u32 data;
56 * other fields. 69 u32 enabled;
57 */ 70 int ret = 0;
58 u32 data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); 71
59 data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK); 72 spin_lock(&mv64x60_wdt_spinlock);
60 data |= val; 73 data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
61 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); 74 enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1;
75
76 /* only toggle the requested field if enabled state matches predicate */
77 if ((enabled ^ enabled_predicate) == 0) {
78 /* We write a 1, then a 2 -- to the appropriate field */
79 data = (1 << field_shift) | mv64x60_wdt_count;
80 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
81
82 data = (2 << field_shift) | mv64x60_wdt_count;
83 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
84 ret = 1;
85 }
86 spin_unlock(&mv64x60_wdt_spinlock);
87
88 return ret;
62} 89}
63 90
64static void mv64x60_wdt_service(void) 91static void mv64x60_wdt_service(void)
65{ 92{
66 /* Write 01 followed by 10 to CTL2 */ 93 mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
67 mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01)); 94 MV64x60_WDC_SERVICE_SHIFT);
68 mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02));
69} 95}
70 96
71static void mv64x60_wdt_handler_disable(void) 97static void mv64x60_wdt_handler_enable(void)
72{ 98{
73 if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { 99 if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE,
74 /* Write 01 followed by 10 to CTL1 */ 100 MV64x60_WDC_ENABLE_SHIFT)) {
75 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); 101 mv64x60_wdt_service();
76 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); 102 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
77 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
78 } 103 }
79} 104}
80 105
81static void mv64x60_wdt_handler_enable(void) 106static void mv64x60_wdt_handler_disable(void)
82{ 107{
83 if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { 108 if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
84 /* Write 01 followed by 10 to CTL1 */ 109 MV64x60_WDC_ENABLE_SHIFT))
85 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); 110 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
86 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
87 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
88 }
89} 111}
90 112
91static void mv64x60_wdt_set_timeout(int timeout) 113static void mv64x60_wdt_set_timeout(unsigned int timeout)
92{ 114{
93 /* maximum bus cycle count is 0xFFFFFFFF */ 115 /* maximum bus cycle count is 0xFFFFFFFF */
94 if (timeout > 0xFFFFFFFF / bus_clk) 116 if (timeout > 0xFFFFFFFF / bus_clk)
95 timeout = 0xFFFFFFFF / bus_clk; 117 timeout = 0xFFFFFFFF / bus_clk;
96 118
119 mv64x60_wdt_count = timeout * bus_clk >> 8;
97 mv64x60_wdt_timeout = timeout; 120 mv64x60_wdt_timeout = timeout;
98 writel((timeout * bus_clk) >> 8,
99 mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
100 mv64x60_wdt_service();
101} 121}
102 122
103static int mv64x60_wdt_open(struct inode *inode, struct file *file) 123static int mv64x60_wdt_open(struct inode *inode, struct file *file)
@@ -108,7 +128,6 @@ static int mv64x60_wdt_open(struct inode *inode, struct file *file)
108 if (nowayout) 128 if (nowayout)
109 __module_get(THIS_MODULE); 129 __module_get(THIS_MODULE);
110 130
111 mv64x60_wdt_service();
112 mv64x60_wdt_handler_enable(); 131 mv64x60_wdt_handler_enable();
113 132
114 return nonseekable_open(inode, file); 133 return nonseekable_open(inode, file);
@@ -270,7 +289,6 @@ static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
270{ 289{
271 misc_deregister(&mv64x60_wdt_miscdev); 290 misc_deregister(&mv64x60_wdt_miscdev);
272 291
273 mv64x60_wdt_service();
274 mv64x60_wdt_handler_disable(); 292 mv64x60_wdt_handler_disable();
275 293
276 iounmap(mv64x60_wdt_regs); 294 iounmap(mv64x60_wdt_regs);