cs5535-gpio: add AMD CS5535/CS5536 GPIO driver support
[safe/jmp/linux-2.6] / drivers / gpio / cs5535-gpio.c
1 /*
2  * AMD CS5535/CS5536 GPIO driver
3  * Copyright (C) 2006  Advanced Micro Devices, Inc.
4  * Copyright (C) 2007-2009  Andres Salomon <dilinger@collabora.co.uk>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU General Public License
8  * as published by the Free Software Foundation.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/spinlock.h>
13 #include <linux/module.h>
14 #include <linux/pci.h>
15 #include <linux/gpio.h>
16 #include <linux/io.h>
17 #include <linux/cs5535.h>
18
19 #define DRV_NAME "cs5535-gpio"
20 #define GPIO_BAR 1
21
22 static struct cs5535_gpio_chip {
23         struct gpio_chip chip;
24         resource_size_t base;
25
26         struct pci_dev *pdev;
27         spinlock_t lock;
28 } cs5535_gpio_chip;
29
30 /*
31  * The CS5535/CS5536 GPIOs support a number of extra features not defined
32  * by the gpio_chip API, so these are exported.  For a full list of the
33  * registers, see include/linux/cs5535.h.
34  */
35
36 static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
37                 unsigned int reg)
38 {
39         if (offset < 16)
40                 /* low bank register */
41                 outl(1 << offset, chip->base + reg);
42         else
43                 /* high bank register */
44                 outl(1 << (offset - 16), chip->base + 0x80 + reg);
45 }
46
47 void cs5535_gpio_set(unsigned offset, unsigned int reg)
48 {
49         struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
50         unsigned long flags;
51
52         spin_lock_irqsave(&chip->lock, flags);
53         __cs5535_gpio_set(chip, offset, reg);
54         spin_unlock_irqrestore(&chip->lock, flags);
55 }
56 EXPORT_SYMBOL_GPL(cs5535_gpio_set);
57
58 static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
59                 unsigned int reg)
60 {
61         if (offset < 16)
62                 /* low bank register */
63                 outl(1 << (offset + 16), chip->base + reg);
64         else
65                 /* high bank register */
66                 outl(1 << offset, chip->base + 0x80 + reg);
67 }
68
69 void cs5535_gpio_clear(unsigned offset, unsigned int reg)
70 {
71         struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
72         unsigned long flags;
73
74         spin_lock_irqsave(&chip->lock, flags);
75         __cs5535_gpio_clear(chip, offset, reg);
76         spin_unlock_irqrestore(&chip->lock, flags);
77 }
78 EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
79
80 int cs5535_gpio_isset(unsigned offset, unsigned int reg)
81 {
82         struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
83         unsigned long flags;
84         long val;
85
86         spin_lock_irqsave(&chip->lock, flags);
87         if (offset < 16)
88                 /* low bank register */
89                 val = inl(chip->base + reg);
90         else {
91                 /* high bank register */
92                 val = inl(chip->base + 0x80 + reg);
93                 offset -= 16;
94         }
95         spin_unlock_irqrestore(&chip->lock, flags);
96
97         return (val & (1 << offset)) ? 1 : 0;
98 }
99 EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
100
101 /*
102  * Generic gpio_chip API support.
103  */
104
105 static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
106 {
107         return cs5535_gpio_isset(offset, GPIO_OUTPUT_VAL);
108 }
109
110 static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
111 {
112         if (val)
113                 cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
114         else
115                 cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
116 }
117
118 static int chip_direction_input(struct gpio_chip *c, unsigned offset)
119 {
120         struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
121         unsigned long flags;
122
123         spin_lock_irqsave(&chip->lock, flags);
124         __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
125         spin_unlock_irqrestore(&chip->lock, flags);
126
127         return 0;
128 }
129
130 static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
131 {
132         struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
133         unsigned long flags;
134
135         spin_lock_irqsave(&chip->lock, flags);
136
137         __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
138         if (val)
139                 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
140         else
141                 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
142
143         spin_unlock_irqrestore(&chip->lock, flags);
144
145         return 0;
146 }
147
148 static struct cs5535_gpio_chip cs5535_gpio_chip = {
149         .chip = {
150                 .owner = THIS_MODULE,
151                 .label = DRV_NAME,
152
153                 .base = 0,
154                 .ngpio = 28,
155
156                 .get = chip_gpio_get,
157                 .set = chip_gpio_set,
158
159                 .direction_input = chip_direction_input,
160                 .direction_output = chip_direction_output,
161         },
162 };
163
164 static int __init cs5535_gpio_probe(struct pci_dev *pdev,
165                 const struct pci_device_id *pci_id)
166 {
167         int err;
168
169         /* There are two ways to get the GPIO base address; one is by
170          * fetching it from MSR_LBAR_GPIO, the other is by reading the
171          * PCI BAR info.  The latter method is easier (especially across
172          * different architectures), so we'll stick with that for now.  If
173          * it turns out to be unreliable in the face of crappy BIOSes, we
174          * can always go back to using MSRs.. */
175
176         err = pci_enable_device_io(pdev);
177         if (err) {
178                 dev_err(&pdev->dev, "can't enable device IO\n");
179                 goto done;
180         }
181
182         err = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
183         if (err) {
184                 dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
185                 goto done;
186         }
187
188         /* set up the driver-specific struct */
189         cs5535_gpio_chip.base = pci_resource_start(pdev, GPIO_BAR);
190         cs5535_gpio_chip.pdev = pdev;
191         spin_lock_init(&cs5535_gpio_chip.lock);
192
193         dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", GPIO_BAR,
194                         (unsigned long long) cs5535_gpio_chip.base);
195
196         /* finally, register with the generic GPIO API */
197         err = gpiochip_add(&cs5535_gpio_chip.chip);
198         if (err) {
199                 dev_err(&pdev->dev, "failed to register gpio chip\n");
200                 goto release_region;
201         }
202
203         printk(KERN_INFO DRV_NAME ": GPIO support successfully loaded.\n");
204         return 0;
205
206 release_region:
207         pci_release_region(pdev, GPIO_BAR);
208 done:
209         return err;
210 }
211
212 static void __exit cs5535_gpio_remove(struct pci_dev *pdev)
213 {
214         int err;
215
216         err = gpiochip_remove(&cs5535_gpio_chip.chip);
217         if (err) {
218                 /* uhh? */
219                 dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
220         }
221         pci_release_region(pdev, GPIO_BAR);
222 }
223
224 static struct pci_device_id cs5535_gpio_pci_tbl[] = {
225         { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
226         { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
227         { 0, },
228 };
229 MODULE_DEVICE_TABLE(pci, cs5535_gpio_pci_tbl);
230
231 /*
232  * We can't use the standard PCI driver registration stuff here, since
233  * that allows only one driver to bind to each PCI device (and we want
234  * multiple drivers to be able to bind to the device).  Instead, manually
235  * scan for the PCI device, request a single region, and keep track of the
236  * devices that we're using.
237  */
238
239 static int __init cs5535_gpio_scan_pci(void)
240 {
241         struct pci_dev *pdev;
242         int err = -ENODEV;
243         int i;
244
245         for (i = 0; i < ARRAY_SIZE(cs5535_gpio_pci_tbl); i++) {
246                 pdev = pci_get_device(cs5535_gpio_pci_tbl[i].vendor,
247                                 cs5535_gpio_pci_tbl[i].device, NULL);
248                 if (pdev) {
249                         err = cs5535_gpio_probe(pdev, &cs5535_gpio_pci_tbl[i]);
250                         if (err)
251                                 pci_dev_put(pdev);
252
253                         /* we only support a single CS5535/6 southbridge */
254                         break;
255                 }
256         }
257
258         return err;
259 }
260
261 static void __exit cs5535_gpio_free_pci(void)
262 {
263         cs5535_gpio_remove(cs5535_gpio_chip.pdev);
264         pci_dev_put(cs5535_gpio_chip.pdev);
265 }
266
267 static int __init cs5535_gpio_init(void)
268 {
269         return cs5535_gpio_scan_pci();
270 }
271
272 static void __exit cs5535_gpio_exit(void)
273 {
274         cs5535_gpio_free_pci();
275 }
276
277 module_init(cs5535_gpio_init);
278 module_exit(cs5535_gpio_exit);
279
280 MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
281 MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
282 MODULE_LICENSE("GPL");