blob: 79eb9c5a2923df4d88fe08643151c0078cf8ed6a [file] [log] [blame]
Andres Salomon5f0a96b2009-12-14 18:00:32 -08001/*
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
Tobias Mueller1ea3fa72009-12-14 18:00:35 -080022/*
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
Andres Salomon5f0a96b2009-12-14 18:00:32 -080045static 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
Andres Salomon00185162010-12-21 13:04:42 -080059static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
60 unsigned int reg)
Andres Salomon853ff882010-12-02 14:31:17 -080061{
Andres Salomon00185162010-12-21 13:04:42 -080062 unsigned long addr = chip->base + 0x80 + reg;
63
Andres Salomon853ff882010-12-02 14:31:17 -080064 /*
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.
Andres Salomon00185162010-12-21 13:04:42 -080069 *
70 * Don't apply this errata to the edge status GPIOs, as writing
71 * to their lower bits will clear them.
Andres Salomon853ff882010-12-02 14:31:17 -080072 */
Andres Salomon00185162010-12-21 13:04:42 -080073 if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS)
74 val |= inl(addr);
Andres Salomon853ff882010-12-02 14:31:17 -080075 outl(val, addr);
76}
77
Andres Salomon5f0a96b2009-12-14 18:00:32 -080078static 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 */
Andres Salomon00185162010-12-21 13:04:42 -080086 errata_outl(chip, 1 << (offset - 16), reg);
Andres Salomon5f0a96b2009-12-14 18:00:32 -080087}
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 */
Andres Salomon00185162010-12-21 13:04:42 -0800108 errata_outl(chip, 1 << offset, reg);
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800109}
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
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800147static 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
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800174static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
175{
Ben Gardnera8a51642010-03-05 13:44:38 -0800176 return cs5535_gpio_isset(offset, GPIO_READ_BACK);
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800177}
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);
Ben Gardnera8a51642010-03-05 13:44:38 -0800194 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800195 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
Ben Gardnera8a51642010-03-05 13:44:38 -0800207 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800208 __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
Uwe Kleine-König62154992010-05-26 14:42:17 -0700219static const char * const cs5535_gpio_names[] = {
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800220 "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
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800230static struct cs5535_gpio_chip cs5535_gpio_chip = {
231 .chip = {
232 .owner = THIS_MODULE,
233 .label = DRV_NAME,
234
235 .base = 0,
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800236 .ngpio = 32,
237 .names = cs5535_gpio_names,
238 .request = chip_gpio_request,
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800239
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;
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800252 ulong mask_orig = mask;
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800253
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
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800281 /* 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
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800292 /* finally, register with the generic GPIO API */
293 err = gpiochip_add(&cs5535_gpio_chip.chip);
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800294 if (err)
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800295 goto release_region;
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800296
Tobias Mueller1ea3fa72009-12-14 18:00:35 -0800297 dev_info(&pdev->dev, DRV_NAME ": GPIO support successfully loaded.\n");
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800298 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
Andres Salomond45840d2010-07-20 13:24:32 -0700374MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
Andres Salomon5f0a96b2009-12-14 18:00:32 -0800375MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
376MODULE_LICENSE("GPL");