OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [amd7xx_tco.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *      AMD 766/768 TCO Timer Driver
3
 *      (c) Copyright 2002 Zwane Mwaikambo <zwane@holomorphy.com>
4
 *      All Rights Reserved.
5
 *
6
 *      Parts from;
7
 *      Hardware driver for the AMD 768 Random Number Generator (RNG)
8
 *      (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
9
 *
10
 *      This program is free software; you can redistribute it and/or
11
 *      modify it under the terms of the GNU General Public License version 2
12
 *      as published by the Free Software Foundation.
13
 *
14
 *      The author(s) of this software shall not be held liable for damages
15
 *      of any nature resulting due to the use of this software. This
16
 *      software is provided AS-IS with no warranties.
17
 *
18
 */
19
 
20
#include <linux/config.h>
21
#include <linux/module.h>
22
#include <linux/version.h>
23
#include <linux/kernel.h>
24
#include <linux/miscdevice.h>
25
#include <linux/watchdog.h>
26
#include <linux/ioport.h>
27
#include <linux/spinlock.h>
28
#include <linux/ioport.h>
29
#include <asm/semaphore.h>
30
#include <asm/io.h>
31
#include <asm/uaccess.h>
32
#include <linux/notifier.h>
33
#include <linux/reboot.h>
34
#include <linux/init.h>
35
#include <linux/pci.h>
36
 
37
#define AMDTCO_MODULE_VER       "build 20021116"
38
#define AMDTCO_MODULE_NAME      "amd7xx_tco"
39
#define PFX                     AMDTCO_MODULE_NAME ": "
40
 
41
#define MAX_TIMEOUT     38      /* max of 38 seconds, although the system will only
42
                                 * reset itself after the second timeout */
43
 
44
/* pmbase registers */
45
#define TCO_RELOAD_REG  0x40            /* bits 0-5 are current count, 6-7 are reserved */
46
#define TCO_INITVAL_REG 0x41            /* bits 0-5 are value to load, 6-7 are reserved */
47
#define TCO_TIMEOUT_MASK        0x3f
48
#define TCO_STATUS1_REG 0x44
49
#define TCO_STATUS2_REG 0x46
50
#define NDTO_STS2       (1 << 1)        /* we're interested in the second timeout */ 
51
#define BOOT_STS        (1 << 2)        /* will be set if NDTO_STS2 was set before reboot */
52
#define TCO_CTRL1_REG   0x48
53
#define TCO_HALT        (1 << 11)
54
#define NO_REBOOT       (1 << 10)       /* in DevB:3x48 */
55
 
56
static char banner[] __initdata = KERN_INFO PFX AMDTCO_MODULE_VER "\n";
57
static int timeout = 38;
58
static u32 pmbase;              /* PMxx I/O base */
59
static struct pci_dev *dev;
60
static struct semaphore open_sem;
61
static spinlock_t amdtco_lock;  /* only for device access */
62
static int expect_close = 0;
63
 
64
MODULE_PARM(timeout, "i");
65
MODULE_PARM_DESC(timeout, "range is 0-38 seconds, default is 38");
66
 
67
static inline u8 seconds_to_ticks(int seconds)
68
{
69
        /* the internal timer is stored as ticks which decrement
70
         * every 0.6 seconds */
71
        return (seconds * 10) / 6;
72
}
73
 
74
static inline int ticks_to_seconds(u8 ticks)
75
{
76
        return (ticks * 6) / 10;
77
}
78
 
79
static inline int amdtco_status(void)
80
{
81
        u16 reg;
82
        int status = 0;
83
 
84
        reg = inb(pmbase+TCO_CTRL1_REG);
85
        if ((reg & TCO_HALT) == 0)
86
                status |= WDIOF_KEEPALIVEPING;
87
 
88
        reg = inb(pmbase+TCO_STATUS2_REG);
89
        if (reg & BOOT_STS)
90
                status |= WDIOF_CARDRESET;
91
 
92
        return status;
93
}
94
 
95
static inline void amdtco_ping(void)
96
{
97
        outb(1, pmbase+TCO_RELOAD_REG);
98
}
99
 
100
static inline int amdtco_gettimeout(void)
101
{
102
        u8 reg = inb(pmbase+TCO_RELOAD_REG) & TCO_TIMEOUT_MASK;
103
        return ticks_to_seconds(reg);
104
}
105
 
106
static inline void amdtco_settimeout(unsigned int timeout)
107
{
108
        u8 reg = seconds_to_ticks(timeout) & TCO_TIMEOUT_MASK;
109
        outb(reg, pmbase+TCO_INITVAL_REG);
110
}
111
 
112
static inline void amdtco_global_enable(void)
113
{
114
        u16 reg;
115
 
116
        spin_lock(&amdtco_lock);
117
 
118
        /* clear NO_REBOOT on DevB:3x48 p97 */
119
        pci_read_config_word(dev, 0x48, &reg);
120
        reg &= ~NO_REBOOT;
121
        pci_write_config_word(dev, 0x48, reg);
122
 
123
        spin_unlock(&amdtco_lock);
124
}
125
 
126
static inline void amdtco_enable(void)
127
{
128
        u16 reg;
129
 
130
        spin_lock(&amdtco_lock);
131
        reg = inw(pmbase+TCO_CTRL1_REG);
132
        reg &= ~TCO_HALT;
133
        outw(reg, pmbase+TCO_CTRL1_REG);
134
        spin_unlock(&amdtco_lock);
135
}
136
 
137
static inline void amdtco_disable(void)
138
{
139
        u16 reg;
140
 
141
        spin_lock(&amdtco_lock);
142
        reg = inw(pmbase+TCO_CTRL1_REG);
143
        reg |= TCO_HALT;
144
        outw(reg, pmbase+TCO_CTRL1_REG);
145
        spin_unlock(&amdtco_lock);
146
}
147
 
148
static int amdtco_fop_open(struct inode *inode, struct file *file)
149
{
150
        if (down_trylock(&open_sem))
151
                return -EBUSY;
152
 
153
#ifdef CONFIG_WATCHDOG_NOWAYOUT 
154
        MOD_INC_USE_COUNT;
155
#endif
156
 
157
        if (timeout > MAX_TIMEOUT)
158
                timeout = MAX_TIMEOUT;
159
 
160
        amdtco_disable();
161
        amdtco_settimeout(timeout);
162
        amdtco_global_enable();
163
        amdtco_enable();
164
        amdtco_ping();
165
        printk(KERN_INFO PFX "Watchdog enabled, timeout = %ds of %ds\n",
166
                amdtco_gettimeout(), timeout);
167
 
168
        return 0;
169
}
170
 
171
 
172
static int amdtco_fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
173
{
174
        int new_timeout;
175
        int tmp;
176
 
177
        static struct watchdog_info ident = {
178
                options:        WDIOF_SETTIMEOUT | WDIOF_CARDRESET,
179
                identity:       "AMD 766/768"
180
        };
181
 
182
        switch (cmd) {
183
                default:
184
                        return -ENOTTY;
185
 
186
                case WDIOC_GETSUPPORT:
187
                        if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident))
188
                                return -EFAULT;
189
                        return 0;
190
 
191
                case WDIOC_GETSTATUS:
192
                        return put_user(amdtco_status(), (int *)arg);
193
 
194
                case WDIOC_KEEPALIVE:
195
                        amdtco_ping();
196
                        return 0;
197
 
198
                case WDIOC_SETTIMEOUT:
199
                        if (get_user(new_timeout, (int *)arg))
200
                                return -EFAULT;
201
 
202
                        if (new_timeout < 0)
203
                                return -EINVAL;
204
 
205
                        if (new_timeout > MAX_TIMEOUT)
206
                                new_timeout = MAX_TIMEOUT;
207
 
208
                        timeout = new_timeout;
209
                        amdtco_settimeout(timeout);
210
                        /* fall through and return the new timeout */
211
 
212
                case WDIOC_GETTIMEOUT:
213
                        return put_user(amdtco_gettimeout(), (int *)arg);
214
 
215
                case WDIOC_SETOPTIONS:
216
                        if (copy_from_user(&tmp, (int *)arg, sizeof tmp))
217
                                return -EFAULT;
218
 
219
                        if (tmp & WDIOS_DISABLECARD)
220
                                amdtco_disable();
221
 
222
                        if (tmp & WDIOS_ENABLECARD)
223
                                amdtco_enable();
224
 
225
                        return 0;
226
        }
227
}
228
 
229
 
230
static int amdtco_fop_release(struct inode *inode, struct file *file)
231
{
232
        if (expect_close) {
233
                amdtco_disable();
234
                printk(KERN_INFO PFX "Watchdog disabled\n");
235
        } else {
236
                amdtco_ping();
237
                printk(KERN_CRIT PFX "Unexpected close!, timeout in %d seconds\n", timeout);
238
        }
239
 
240
        up(&open_sem);
241
        return 0;
242
}
243
 
244
 
245
static ssize_t amdtco_fop_write(struct file *file, const char *data, size_t len, loff_t *ppos)
246
{
247
        if (ppos != &file->f_pos)
248
                return -ESPIPE;
249
 
250
        if (len) {
251
#ifndef CONFIG_WATCHDOG_NOWAYOUT
252
                size_t i;
253
                char c;
254
                expect_close = 0;
255
 
256
                for (i = 0; i != len; i++) {
257
                        if (get_user(c, data + i))
258
                                return -EFAULT;
259
 
260
                        if (c == 'V')
261
                                expect_close = 1;
262
                }
263
#endif
264
                amdtco_ping();
265
        }
266
 
267
        return len;
268
}
269
 
270
 
271
static int amdtco_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
272
{
273
        if (code == SYS_DOWN || code == SYS_HALT)
274
                amdtco_disable();
275
 
276
        return NOTIFY_DONE;
277
}
278
 
279
 
280
static struct notifier_block amdtco_notifier =
281
{
282
        notifier_call:  amdtco_notify_sys
283
};
284
 
285
static struct file_operations amdtco_fops =
286
{
287
        owner:          THIS_MODULE,
288
        write:          amdtco_fop_write,
289
        ioctl:          amdtco_fop_ioctl,
290
        open:           amdtco_fop_open,
291
        release:        amdtco_fop_release
292
};
293
 
294
static struct miscdevice amdtco_miscdev =
295
{
296
        minor:          WATCHDOG_MINOR,
297
        name:           "watchdog",
298
        fops:           &amdtco_fops
299
};
300
 
301
static struct pci_device_id amdtco_pci_tbl[] __initdata = {
302
        { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, },
303
        { 0, }
304
};
305
 
306
MODULE_DEVICE_TABLE (pci, amdtco_pci_tbl);
307
 
308
static int __init amdtco_init(void)
309
{
310
        int ret;
311
 
312
        sema_init(&open_sem, 1);
313
        spin_lock_init(&amdtco_lock);
314
 
315
        pci_for_each_dev(dev) {
316
                if (pci_match_device (amdtco_pci_tbl, dev) != NULL)
317
                        goto found_one;
318
        }
319
 
320
        return -ENODEV;
321
 
322
found_one:
323
 
324
        if ((ret = register_reboot_notifier(&amdtco_notifier))) {
325
                printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret);
326
                goto out_clean;
327
        }
328
 
329
        if ((ret = misc_register(&amdtco_miscdev))) {
330
                printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
331
                goto out_unreg_reboot;
332
        }
333
 
334
        pci_read_config_dword(dev, 0x58, &pmbase);
335
        pmbase &= 0x0000FF00;
336
 
337
        if (pmbase == 0) {
338
                printk (KERN_ERR PFX "power management base not set\n");
339
                ret = -EIO;
340
                goto out_unreg_misc;
341
        }
342
 
343
        /* ret = 0; */
344
        printk(banner);
345
        goto out_clean;
346
 
347
out_unreg_misc:
348
        misc_deregister(&amdtco_miscdev);
349
out_unreg_reboot:
350
        unregister_reboot_notifier(&amdtco_notifier);
351
out_clean:
352
        return ret;
353
}
354
 
355
static void __exit amdtco_exit(void)
356
{
357
        misc_deregister(&amdtco_miscdev);
358
        unregister_reboot_notifier(&amdtco_notifier);
359
}
360
 
361
 
362
#ifndef MODULE
363
static int __init amdtco_setup(char *str)
364
{
365
        int ints[4];
366
 
367
        str = get_options (str, ARRAY_SIZE(ints), ints);
368
        if (ints[0] > 0)
369
                timeout = ints[1];
370
 
371
        if (!timeout || timeout > 38)
372
                timeout = MAX_TIMEOUT;
373
 
374
        return 1;
375
}
376
 
377
__setup("amd7xx_tco=", amdtco_setup);
378
#endif
379
 
380
module_init(amdtco_init);
381
module_exit(amdtco_exit);
382
 
383
MODULE_AUTHOR("Zwane Mwaikambo <zwane@holomorphy.com>");
384
MODULE_DESCRIPTION("AMD 766/768 TCO Timer Driver");
385
MODULE_LICENSE("GPL");
386
EXPORT_NO_SYMBOLS;
387
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.