cs5535-gpio: don't apply errata #36 to edge detect GPIOs
[deliverable/linux.git] / drivers / gpio / cs5535-gpio.c
CommitLineData
5f0a96b0
AS
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
1ea3fa7b
TM
22/*
23 * Some GPIO pins
24 * 31-29,23 : reserved (always mask out)
25 * 28 : Power Button
26 * 26 : PME#
27 * 22-16 : LPC
28 * 14,15 : SMBus
29 * 9,8 : UART1
30 * 7 : PCI INTB
31 * 3,4 : UART2/DDC
32 * 2 : IDE_IRQ0
33 * 1 : AC_BEEP
34 * 0 : PCI INTA
35 *
36 * If a mask was not specified, allow all except
37 * reserved and Power Button
38 */
39#define GPIO_DEFAULT_MASK 0x0F7FFFFF
40
41static ulong mask = GPIO_DEFAULT_MASK;
42module_param_named(mask, mask, ulong, 0444);
43MODULE_PARM_DESC(mask, "GPIO channel mask.");
44
5f0a96b0
AS
45static struct cs5535_gpio_chip {
46 struct gpio_chip chip;
47 resource_size_t base;
48
49 struct pci_dev *pdev;
50 spinlock_t lock;
51} cs5535_gpio_chip;
52
53/*
54 * The CS5535/CS5536 GPIOs support a number of extra features not defined
55 * by the gpio_chip API, so these are exported. For a full list of the
56 * registers, see include/linux/cs5535.h.
57 */
58
00185165
AS
59static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
60 unsigned int reg)
853ff883 61{
00185165
AS
62 unsigned long addr = chip->base + 0x80 + reg;
63
853ff883
AS
64 /*
65 * According to the CS5536 errata (#36), after suspend
66 * a write to the high bank GPIO register will clear all
67 * non-selected bits; the recommended workaround is a
68 * read-modify-write operation.
00185165
AS
69 *
70 * Don't apply this errata to the edge status GPIOs, as writing
71 * to their lower bits will clear them.
853ff883 72 */
00185165
AS
73 if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS)
74 val |= inl(addr);
853ff883
AS
75 outl(val, addr);
76}
77
5f0a96b0
AS
78static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
79 unsigned int reg)
80{
81 if (offset < 16)
82 /* low bank register */
83 outl(1 << offset, chip->base + reg);
84 else
85 /* high bank register */
00185165 86 errata_outl(chip, 1 << (offset - 16), reg);
5f0a96b0
AS
87}
88
89void cs5535_gpio_set(unsigned offset, unsigned int reg)
90{
91 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
92 unsigned long flags;
93
94 spin_lock_irqsave(&chip->lock, flags);
95 __cs5535_gpio_set(chip, offset, reg);
96 spin_unlock_irqrestore(&chip->lock, flags);
97}
98EXPORT_SYMBOL_GPL(cs5535_gpio_set);
99
100static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
101 unsigned int reg)
102{
103 if (offset < 16)
104 /* low bank register */
105 outl(1 << (offset + 16), chip->base + reg);
106 else
107 /* high bank register */
00185165 108 errata_outl(chip, 1 << offset, reg);
5f0a96b0
AS
109}
110
111void cs5535_gpio_clear(unsigned offset, unsigned int reg)
112{
113 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
114 unsigned long flags;
115
116 spin_lock_irqsave(&chip->lock, flags);
117 __cs5535_gpio_clear(chip, offset, reg);
118 spin_unlock_irqrestore(&chip->lock, flags);
119}
120EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
121
122int cs5535_gpio_isset(unsigned offset, unsigned int reg)
123{
124 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
125 unsigned long flags;
126 long val;
127
128 spin_lock_irqsave(&chip->lock, flags);
129 if (offset < 16)
130 /* low bank register */
131 val = inl(chip->base + reg);
132 else {
133 /* high bank register */
134 val = inl(chip->base + 0x80 + reg);
135 offset -= 16;
136 }
137 spin_unlock_irqrestore(&chip->lock, flags);
138
139 return (val & (1 << offset)) ? 1 : 0;
140}
141EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
142
143/*
144 * Generic gpio_chip API support.
145 */
146
1ea3fa7b
TM
147static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
148{
149 struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
150 unsigned long flags;
151
152 spin_lock_irqsave(&chip->lock, flags);
153
154 /* check if this pin is available */
155 if ((mask & (1 << offset)) == 0) {
156 dev_info(&chip->pdev->dev,
157 "pin %u is not available (check mask)\n", offset);
158 spin_unlock_irqrestore(&chip->lock, flags);
159 return -EINVAL;
160 }
161
162 /* disable output aux 1 & 2 on this pin */
163 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
164 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
165
166 /* disable input aux 1 on this pin */
167 __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
168
169 spin_unlock_irqrestore(&chip->lock, flags);
170
171 return 0;
172}
173
5f0a96b0
AS
174static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
175{
a8a5164c 176 return cs5535_gpio_isset(offset, GPIO_READ_BACK);
5f0a96b0
AS
177}
178
179static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
180{
181 if (val)
182 cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
183 else
184 cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
185}
186
187static int chip_direction_input(struct gpio_chip *c, unsigned offset)
188{
189 struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
190 unsigned long flags;
191
192 spin_lock_irqsave(&chip->lock, flags);
193 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
a8a5164c 194 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
5f0a96b0
AS
195 spin_unlock_irqrestore(&chip->lock, flags);
196
197 return 0;
198}
199
200static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
201{
202 struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
203 unsigned long flags;
204
205 spin_lock_irqsave(&chip->lock, flags);
206
a8a5164c 207 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
5f0a96b0
AS
208 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
209 if (val)
210 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
211 else
212 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
213
214 spin_unlock_irqrestore(&chip->lock, flags);
215
216 return 0;
217}
218
62154991 219static const char * const cs5535_gpio_names[] = {
1ea3fa7b
TM
220 "GPIO0", "GPIO1", "GPIO2", "GPIO3",
221 "GPIO4", "GPIO5", "GPIO6", "GPIO7",
222 "GPIO8", "GPIO9", "GPIO10", "GPIO11",
223 "GPIO12", "GPIO13", "GPIO14", "GPIO15",
224 "GPIO16", "GPIO17", "GPIO18", "GPIO19",
225 "GPIO20", "GPIO21", "GPIO22", NULL,
226 "GPIO24", "GPIO25", "GPIO26", "GPIO27",
227 "GPIO28", NULL, NULL, NULL,
228};
229
5f0a96b0
AS
230static struct cs5535_gpio_chip cs5535_gpio_chip = {
231 .chip = {
232 .owner = THIS_MODULE,
233 .label = DRV_NAME,
234
235 .base = 0,
1ea3fa7b
TM
236 .ngpio = 32,
237 .names = cs5535_gpio_names,
238 .request = chip_gpio_request,
5f0a96b0
AS
239
240 .get = chip_gpio_get,
241 .set = chip_gpio_set,
242
243 .direction_input = chip_direction_input,
244 .direction_output = chip_direction_output,
245 },
246};
247
248static int __init cs5535_gpio_probe(struct pci_dev *pdev,
249 const struct pci_device_id *pci_id)
250{
251 int err;
1ea3fa7b 252 ulong mask_orig = mask;
5f0a96b0
AS
253
254 /* There are two ways to get the GPIO base address; one is by
255 * fetching it from MSR_LBAR_GPIO, the other is by reading the
256 * PCI BAR info. The latter method is easier (especially across
257 * different architectures), so we'll stick with that for now. If
258 * it turns out to be unreliable in the face of crappy BIOSes, we
259 * can always go back to using MSRs.. */
260
261 err = pci_enable_device_io(pdev);
262 if (err) {
263 dev_err(&pdev->dev, "can't enable device IO\n");
264 goto done;
265 }
266
267 err = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
268 if (err) {
269 dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
270 goto done;
271 }
272
273 /* set up the driver-specific struct */
274 cs5535_gpio_chip.base = pci_resource_start(pdev, GPIO_BAR);
275 cs5535_gpio_chip.pdev = pdev;
276 spin_lock_init(&cs5535_gpio_chip.lock);
277
278 dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", GPIO_BAR,
279 (unsigned long long) cs5535_gpio_chip.base);
280
1ea3fa7b
TM
281 /* mask out reserved pins */
282 mask &= 0x1F7FFFFF;
283
284 /* do not allow pin 28, Power Button, as there's special handling
285 * in the PMC needed. (note 12, p. 48) */
286 mask &= ~(1 << 28);
287
288 if (mask_orig != mask)
289 dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
290 mask_orig, mask);
291
5f0a96b0
AS
292 /* finally, register with the generic GPIO API */
293 err = gpiochip_add(&cs5535_gpio_chip.chip);
1ea3fa7b 294 if (err)
5f0a96b0 295 goto release_region;
5f0a96b0 296
1ea3fa7b 297 dev_info(&pdev->dev, DRV_NAME ": GPIO support successfully loaded.\n");
5f0a96b0
AS
298 return 0;
299
300release_region:
301 pci_release_region(pdev, GPIO_BAR);
302done:
303 return err;
304}
305
306static void __exit cs5535_gpio_remove(struct pci_dev *pdev)
307{
308 int err;
309
310 err = gpiochip_remove(&cs5535_gpio_chip.chip);
311 if (err) {
312 /* uhh? */
313 dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
314 }
315 pci_release_region(pdev, GPIO_BAR);
316}
317
318static struct pci_device_id cs5535_gpio_pci_tbl[] = {
319 { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
320 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
321 { 0, },
322};
323MODULE_DEVICE_TABLE(pci, cs5535_gpio_pci_tbl);
324
325/*
326 * We can't use the standard PCI driver registration stuff here, since
327 * that allows only one driver to bind to each PCI device (and we want
328 * multiple drivers to be able to bind to the device). Instead, manually
329 * scan for the PCI device, request a single region, and keep track of the
330 * devices that we're using.
331 */
332
333static int __init cs5535_gpio_scan_pci(void)
334{
335 struct pci_dev *pdev;
336 int err = -ENODEV;
337 int i;
338
339 for (i = 0; i < ARRAY_SIZE(cs5535_gpio_pci_tbl); i++) {
340 pdev = pci_get_device(cs5535_gpio_pci_tbl[i].vendor,
341 cs5535_gpio_pci_tbl[i].device, NULL);
342 if (pdev) {
343 err = cs5535_gpio_probe(pdev, &cs5535_gpio_pci_tbl[i]);
344 if (err)
345 pci_dev_put(pdev);
346
347 /* we only support a single CS5535/6 southbridge */
348 break;
349 }
350 }
351
352 return err;
353}
354
355static void __exit cs5535_gpio_free_pci(void)
356{
357 cs5535_gpio_remove(cs5535_gpio_chip.pdev);
358 pci_dev_put(cs5535_gpio_chip.pdev);
359}
360
361static int __init cs5535_gpio_init(void)
362{
363 return cs5535_gpio_scan_pci();
364}
365
366static void __exit cs5535_gpio_exit(void)
367{
368 cs5535_gpio_free_pci();
369}
370
371module_init(cs5535_gpio_init);
372module_exit(cs5535_gpio_exit);
373
d45840d9 374MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
5f0a96b0
AS
375MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
376MODULE_LICENSE("GPL");
This page took 0.116086 seconds and 5 git commands to generate.