[BCM43XX]: Change radio hardware switch status printk from debug to regular
[linux-2.6] / drivers / char / cs5535_gpio.c
1 /*
2  * AMD CS5535/CS5536 GPIO driver.
3  * Allows a user space process to play with the GPIO pins.
4  *
5  * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the smems of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  */
11
12 #include <linux/fs.h>
13 #include <linux/module.h>
14 #include <linux/errno.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
17 #include <linux/cdev.h>
18 #include <linux/ioport.h>
19 #include <linux/pci.h>
20 #include <asm/uaccess.h>
21 #include <asm/io.h>
22
23
24 #define NAME                    "cs5535_gpio"
25
26 MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
27 MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO Pin Driver");
28 MODULE_LICENSE("GPL");
29
30 static int major;
31 module_param(major, int, 0);
32 MODULE_PARM_DESC(major, "Major device number");
33
34 static ulong mask;
35 module_param(mask, ulong, 0);
36 MODULE_PARM_DESC(mask, "GPIO channel mask");
37
38 #define MSR_LBAR_GPIO           0x5140000C
39
40 static u32 gpio_base;
41
42 static struct pci_device_id divil_pci[] = {
43         { PCI_DEVICE(PCI_VENDOR_ID_NS,  PCI_DEVICE_ID_NS_CS5535_ISA) },
44         { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
45         { } /* NULL entry */
46 };
47 MODULE_DEVICE_TABLE(pci, divil_pci);
48
49 static struct cdev cs5535_gpio_cdev;
50
51 /* reserve 32 entries even though some aren't usable */
52 #define CS5535_GPIO_COUNT       32
53
54 /* IO block size */
55 #define CS5535_GPIO_SIZE        256
56
57 struct gpio_regmap {
58         u32     rd_offset;
59         u32     wr_offset;
60         char    on;
61         char    off;
62 };
63 static struct gpio_regmap rm[] =
64 {
65         { 0x30, 0x00, '1', '0' },       /* GPIOx_READ_BACK / GPIOx_OUT_VAL */
66         { 0x20, 0x20, 'I', 'i' },       /* GPIOx_IN_EN */
67         { 0x04, 0x04, 'O', 'o' },       /* GPIOx_OUT_EN */
68         { 0x08, 0x08, 't', 'T' },       /* GPIOx_OUT_OD_EN */
69         { 0x18, 0x18, 'P', 'p' },       /* GPIOx_OUT_PU_EN */
70         { 0x1c, 0x1c, 'D', 'd' },       /* GPIOx_OUT_PD_EN */
71 };
72
73
74 /**
75  * Gets the register offset for the GPIO bank.
76  * Low (0-15) starts at 0x00, high (16-31) starts at 0x80
77  */
78 static inline u32 cs5535_lowhigh_base(int reg)
79 {
80         return (reg & 0x10) << 3;
81 }
82
83 static ssize_t cs5535_gpio_write(struct file *file, const char __user *data,
84                                  size_t len, loff_t *ppos)
85 {
86         u32     m = iminor(file->f_path.dentry->d_inode);
87         int     i, j;
88         u32     base = gpio_base + cs5535_lowhigh_base(m);
89         u32     m0, m1;
90         char    c;
91
92         /**
93          * Creates the mask for atomic bit programming.
94          * The high 16 bits and the low 16 bits are used to set the mask.
95          * For example, GPIO 15 maps to 31,15: 0,1 => On; 1,0=> Off
96          */
97         m1 = 1 << (m & 0x0F);
98         m0 = m1 << 16;
99
100         for (i = 0; i < len; ++i) {
101                 if (get_user(c, data+i))
102                         return -EFAULT;
103
104                 for (j = 0; j < ARRAY_SIZE(rm); j++) {
105                         if (c == rm[j].on) {
106                                 outl(m1, base + rm[j].wr_offset);
107                                 break;
108                         } else if (c == rm[j].off) {
109                                 outl(m0, base + rm[j].wr_offset);
110                                 break;
111                         }
112                 }
113         }
114         *ppos = 0;
115         return len;
116 }
117
118 static ssize_t cs5535_gpio_read(struct file *file, char __user *buf,
119                                 size_t len, loff_t *ppos)
120 {
121         u32     m = iminor(file->f_path.dentry->d_inode);
122         u32     base = gpio_base + cs5535_lowhigh_base(m);
123         int     rd_bit = 1 << (m & 0x0f);
124         int     i;
125         char    ch;
126         ssize_t count = 0;
127
128         if (*ppos >= ARRAY_SIZE(rm))
129                 return 0;
130
131         for (i = *ppos; (i < (*ppos + len)) && (i < ARRAY_SIZE(rm)); i++) {
132                 ch = (inl(base + rm[i].rd_offset) & rd_bit) ?
133                      rm[i].on : rm[i].off;
134
135                 if (put_user(ch, buf+count))
136                         return -EFAULT;
137
138                 count++;
139         }
140
141         /* add a line-feed if there is room */
142         if ((i == ARRAY_SIZE(rm)) && (count < len)) {
143                 put_user('\n', buf + count);
144                 count++;
145         }
146
147         *ppos += count;
148         return count;
149 }
150
151 static int cs5535_gpio_open(struct inode *inode, struct file *file)
152 {
153         u32 m = iminor(inode);
154
155         /* the mask says which pins are usable by this driver */
156         if ((mask & (1 << m)) == 0)
157                 return -EINVAL;
158
159         return nonseekable_open(inode, file);
160 }
161
162 static const struct file_operations cs5535_gpio_fops = {
163         .owner  = THIS_MODULE,
164         .write  = cs5535_gpio_write,
165         .read   = cs5535_gpio_read,
166         .open   = cs5535_gpio_open
167 };
168
169 static int __init cs5535_gpio_init(void)
170 {
171         dev_t   dev_id;
172         u32     low, hi;
173         int     retval;
174
175         if (pci_dev_present(divil_pci) == 0) {
176                 printk(KERN_WARNING NAME ": DIVIL not found\n");
177                 return -ENODEV;
178         }
179
180         /* Grab the GPIO I/O range */
181         rdmsr(MSR_LBAR_GPIO, low, hi);
182
183         /* Check the mask and whether GPIO is enabled (sanity check) */
184         if (hi != 0x0000f001) {
185                 printk(KERN_WARNING NAME ": GPIO not enabled\n");
186                 return -ENODEV;
187         }
188
189         /* Mask off the IO base address */
190         gpio_base = low & 0x0000ff00;
191
192         /**
193          * Some GPIO pins
194          *  31-29,23 : reserved (always mask out)
195          *  28       : Power Button
196          *  26       : PME#
197          *  22-16    : LPC
198          *  14,15    : SMBus
199          *  9,8      : UART1
200          *  7        : PCI INTB
201          *  3,4      : UART2/DDC
202          *  2        : IDE_IRQ0
203          *  0        : PCI INTA
204          *
205          * If a mask was not specified, be conservative and only allow:
206          *  1,2,5,6,10-13,24,25,27
207          */
208         if (mask != 0)
209                 mask &= 0x1f7fffff;
210         else
211                 mask = 0x0b003c66;
212
213         if (request_region(gpio_base, CS5535_GPIO_SIZE, NAME) == 0) {
214                 printk(KERN_ERR NAME ": can't allocate I/O for GPIO\n");
215                 return -ENODEV;
216         }
217
218         if (major) {
219                 dev_id = MKDEV(major, 0);
220                 retval = register_chrdev_region(dev_id, CS5535_GPIO_COUNT,
221                                                 NAME);
222         } else {
223                 retval = alloc_chrdev_region(&dev_id, 0, CS5535_GPIO_COUNT,
224                                              NAME);
225                 major = MAJOR(dev_id);
226         }
227
228         if (retval) {
229                 release_region(gpio_base, CS5535_GPIO_SIZE);
230                 return -1;
231         }
232
233         printk(KERN_DEBUG NAME ": base=%#x mask=%#lx major=%d\n",
234                gpio_base, mask, major);
235
236         cdev_init(&cs5535_gpio_cdev, &cs5535_gpio_fops);
237         cdev_add(&cs5535_gpio_cdev, dev_id, CS5535_GPIO_COUNT);
238
239         return 0;
240 }
241
242 static void __exit cs5535_gpio_cleanup(void)
243 {
244         dev_t dev_id = MKDEV(major, 0);
245
246         cdev_del(&cs5535_gpio_cdev);
247         unregister_chrdev_region(dev_id, CS5535_GPIO_COUNT);
248         release_region(gpio_base, CS5535_GPIO_SIZE);
249 }
250
251 module_init(cs5535_gpio_init);
252 module_exit(cs5535_gpio_cleanup);