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>
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.
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>
17 #include <linux/cs5535.h>
19 #define DRV_NAME "cs5535-gpio"
24 * 31-29,23 : reserved (always mask out)
36 * If a mask was not specified, allow all except
37 * reserved and Power Button
39 #define GPIO_DEFAULT_MASK 0x0F7FFFFF
41 static ulong mask = GPIO_DEFAULT_MASK;
42 module_param_named(mask, mask, ulong, 0444);
43 MODULE_PARM_DESC(mask, "GPIO channel mask.");
45 static struct cs5535_gpio_chip {
46 struct gpio_chip chip;
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.
59 static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
63 /* low bank register */
64 outl(1 << offset, chip->base + reg);
66 /* high bank register */
67 outl(1 << (offset - 16), chip->base + 0x80 + reg);
70 void cs5535_gpio_set(unsigned offset, unsigned int reg)
72 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
75 spin_lock_irqsave(&chip->lock, flags);
76 __cs5535_gpio_set(chip, offset, reg);
77 spin_unlock_irqrestore(&chip->lock, flags);
79 EXPORT_SYMBOL_GPL(cs5535_gpio_set);
81 static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
85 /* low bank register */
86 outl(1 << (offset + 16), chip->base + reg);
88 /* high bank register */
89 outl(1 << offset, chip->base + 0x80 + reg);
92 void cs5535_gpio_clear(unsigned offset, unsigned int reg)
94 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
97 spin_lock_irqsave(&chip->lock, flags);
98 __cs5535_gpio_clear(chip, offset, reg);
99 spin_unlock_irqrestore(&chip->lock, flags);
101 EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
103 int cs5535_gpio_isset(unsigned offset, unsigned int reg)
105 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
109 spin_lock_irqsave(&chip->lock, flags);
111 /* low bank register */
112 val = inl(chip->base + reg);
114 /* high bank register */
115 val = inl(chip->base + 0x80 + reg);
118 spin_unlock_irqrestore(&chip->lock, flags);
120 return (val & (1 << offset)) ? 1 : 0;
122 EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
125 * Generic gpio_chip API support.
128 static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
130 struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
133 spin_lock_irqsave(&chip->lock, flags);
135 /* check if this pin is available */
136 if ((mask & (1 << offset)) == 0) {
137 dev_info(&chip->pdev->dev,
138 "pin %u is not available (check mask)\n", offset);
139 spin_unlock_irqrestore(&chip->lock, flags);
143 /* disable output aux 1 & 2 on this pin */
144 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
145 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
147 /* disable input aux 1 on this pin */
148 __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
150 spin_unlock_irqrestore(&chip->lock, flags);
155 static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
157 return cs5535_gpio_isset(offset, GPIO_READ_BACK);
160 static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
163 cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
165 cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
168 static int chip_direction_input(struct gpio_chip *c, unsigned offset)
170 struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
173 spin_lock_irqsave(&chip->lock, flags);
174 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
175 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
176 spin_unlock_irqrestore(&chip->lock, flags);
181 static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
183 struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
186 spin_lock_irqsave(&chip->lock, flags);
188 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
189 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
191 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
193 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
195 spin_unlock_irqrestore(&chip->lock, flags);
200 static const char * const cs5535_gpio_names[] = {
201 "GPIO0", "GPIO1", "GPIO2", "GPIO3",
202 "GPIO4", "GPIO5", "GPIO6", "GPIO7",
203 "GPIO8", "GPIO9", "GPIO10", "GPIO11",
204 "GPIO12", "GPIO13", "GPIO14", "GPIO15",
205 "GPIO16", "GPIO17", "GPIO18", "GPIO19",
206 "GPIO20", "GPIO21", "GPIO22", NULL,
207 "GPIO24", "GPIO25", "GPIO26", "GPIO27",
208 "GPIO28", NULL, NULL, NULL,
211 static struct cs5535_gpio_chip cs5535_gpio_chip = {
213 .owner = THIS_MODULE,
218 .names = cs5535_gpio_names,
219 .request = chip_gpio_request,
221 .get = chip_gpio_get,
222 .set = chip_gpio_set,
224 .direction_input = chip_direction_input,
225 .direction_output = chip_direction_output,
229 static int __init cs5535_gpio_probe(struct pci_dev *pdev,
230 const struct pci_device_id *pci_id)
233 ulong mask_orig = mask;
235 /* There are two ways to get the GPIO base address; one is by
236 * fetching it from MSR_LBAR_GPIO, the other is by reading the
237 * PCI BAR info. The latter method is easier (especially across
238 * different architectures), so we'll stick with that for now. If
239 * it turns out to be unreliable in the face of crappy BIOSes, we
240 * can always go back to using MSRs.. */
242 err = pci_enable_device_io(pdev);
244 dev_err(&pdev->dev, "can't enable device IO\n");
248 err = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
250 dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
254 /* set up the driver-specific struct */
255 cs5535_gpio_chip.base = pci_resource_start(pdev, GPIO_BAR);
256 cs5535_gpio_chip.pdev = pdev;
257 spin_lock_init(&cs5535_gpio_chip.lock);
259 dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", GPIO_BAR,
260 (unsigned long long) cs5535_gpio_chip.base);
262 /* mask out reserved pins */
265 /* do not allow pin 28, Power Button, as there's special handling
266 * in the PMC needed. (note 12, p. 48) */
269 if (mask_orig != mask)
270 dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
273 /* finally, register with the generic GPIO API */
274 err = gpiochip_add(&cs5535_gpio_chip.chip);
278 dev_info(&pdev->dev, DRV_NAME ": GPIO support successfully loaded.\n");
282 pci_release_region(pdev, GPIO_BAR);
287 static void __exit cs5535_gpio_remove(struct pci_dev *pdev)
291 err = gpiochip_remove(&cs5535_gpio_chip.chip);
294 dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
296 pci_release_region(pdev, GPIO_BAR);
299 static struct pci_device_id cs5535_gpio_pci_tbl[] = {
300 { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
301 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
304 MODULE_DEVICE_TABLE(pci, cs5535_gpio_pci_tbl);
307 * We can't use the standard PCI driver registration stuff here, since
308 * that allows only one driver to bind to each PCI device (and we want
309 * multiple drivers to be able to bind to the device). Instead, manually
310 * scan for the PCI device, request a single region, and keep track of the
311 * devices that we're using.
314 static int __init cs5535_gpio_scan_pci(void)
316 struct pci_dev *pdev;
320 for (i = 0; i < ARRAY_SIZE(cs5535_gpio_pci_tbl); i++) {
321 pdev = pci_get_device(cs5535_gpio_pci_tbl[i].vendor,
322 cs5535_gpio_pci_tbl[i].device, NULL);
324 err = cs5535_gpio_probe(pdev, &cs5535_gpio_pci_tbl[i]);
328 /* we only support a single CS5535/6 southbridge */
336 static void __exit cs5535_gpio_free_pci(void)
338 cs5535_gpio_remove(cs5535_gpio_chip.pdev);
339 pci_dev_put(cs5535_gpio_chip.pdev);
342 static int __init cs5535_gpio_init(void)
344 return cs5535_gpio_scan_pci();
347 static void __exit cs5535_gpio_exit(void)
349 cs5535_gpio_free_pci();
352 module_init(cs5535_gpio_init);
353 module_exit(cs5535_gpio_exit);
355 MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
356 MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
357 MODULE_LICENSE("GPL");