aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2012-06-01 09:36:31 -0400
committerLinus Walleij <linus.walleij@linaro.org>2012-07-12 07:40:13 -0400
commitf942a7de047d8c599cc1a9a26293c8c7400450ea (patch)
treeed79d428fc1e024bcefdb69ed87e6ac1554d3091 /drivers/gpio
parent5e67cc4041304a0dc77e6cfbb355d69eefb883d0 (diff)
gpio: add a driver for GPIO pins found on AMD-8111 south bridge chips
Add a driver to use GPIO pins available on several AMD south bridges (currently only AMD 8111 is supported). Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig12
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-amd8111.c246
3 files changed, 259 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index c4067d0141f..c2dfa9f90b4 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -466,6 +466,18 @@ config GPIO_BT8XX
466 466
467 If unsure, say N. 467 If unsure, say N.
468 468
469config GPIO_AMD8111
470 tristate "AMD 8111 GPIO driver"
471 depends on PCI
472 help
473 The AMD 8111 south bridge contains 32 GPIO pins which can be used.
474
475 Note, that usually system firmware/ACPI handles GPIO pins on their
476 own and users might easily break their systems with uncarefull usage
477 of this driver!
478
479 If unsure, say N
480
469config GPIO_LANGWELL 481config GPIO_LANGWELL
470 bool "Intel Langwell/Penwell GPIO support" 482 bool "Intel Langwell/Penwell GPIO support"
471 depends on PCI && X86 483 depends on PCI && X86
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0f55662002c..3a95b17dae8 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
12obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o 12obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
13obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o 13obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
14obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o 14obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
15obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
15obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o 16obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
16obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o 17obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
17obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o 18obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c
new file mode 100644
index 00000000000..710fafcdd1b
--- /dev/null
+++ b/drivers/gpio/gpio-amd8111.c
@@ -0,0 +1,246 @@
1/*
2 * GPIO driver for AMD 8111 south bridges
3 *
4 * Copyright (c) 2012 Dmitry Eremin-Solenikov
5 *
6 * Based on the AMD RNG driver:
7 * Copyright 2005 (c) MontaVista Software, Inc.
8 * with the majority of the code coming from:
9 *
10 * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
11 * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
12 *
13 * derived from
14 *
15 * Hardware driver for the AMD 768 Random Number Generator (RNG)
16 * (c) Copyright 2001 Red Hat Inc
17 *
18 * derived from
19 *
20 * Hardware driver for Intel i810 Random Number Generator (RNG)
21 * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
22 * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
23 *
24 * This file is licensed under the terms of the GNU General Public
25 * License version 2. This program is licensed "as is" without any
26 * warranty of any kind, whether express or implied.
27 */
28#include <linux/module.h>
29#include <linux/kernel.h>
30#include <linux/gpio.h>
31#include <linux/pci.h>
32#include <linux/spinlock.h>
33
34#define PMBASE_OFFSET 0xb0
35#define PMBASE_SIZE 0x30
36
37#define AMD_REG_GPIO(i) (0x10 + (i))
38
39#define AMD_GPIO_LTCH_STS 0x40 /* Latch status, w1 */
40#define AMD_GPIO_RTIN 0x20 /* Real Time in, ro */
41#define AMD_GPIO_DEBOUNCE 0x10 /* Debounce, rw */
42#define AMD_GPIO_MODE_MASK 0x0c /* Pin Mode Select, rw */
43#define AMD_GPIO_MODE_IN 0x00
44#define AMD_GPIO_MODE_OUT 0x04
45/* Enable alternative (e.g. clkout, IRQ, etc) function of the pin */
46#define AMD_GPIO_MODE_ALTFN 0x08 /* Or 0x09 */
47#define AMD_GPIO_X_MASK 0x03 /* In/Out specific, rw */
48#define AMD_GPIO_X_IN_ACTIVEHI 0x01 /* Active High */
49#define AMD_GPIO_X_IN_LATCH 0x02 /* Latched version is selected */
50#define AMD_GPIO_X_OUT_LOW 0x00
51#define AMD_GPIO_X_OUT_HI 0x01
52#define AMD_GPIO_X_OUT_CLK0 0x02
53#define AMD_GPIO_X_OUT_CLK1 0x03
54
55/*
56 * Data for PCI driver interface
57 *
58 * This data only exists for exporting the supported
59 * PCI ids via MODULE_DEVICE_TABLE. We do not actually
60 * register a pci_driver, because someone else might one day
61 * want to register another driver on the same PCI id.
62 */
63static DEFINE_PCI_DEVICE_TABLE(pci_tbl) = {
64 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 },
65 { 0, }, /* terminate list */
66};
67MODULE_DEVICE_TABLE(pci, pci_tbl);
68
69struct amd_gpio {
70 struct gpio_chip chip;
71 u32 pmbase;
72 void __iomem *pm;
73 struct pci_dev *pdev;
74 spinlock_t lock; /* guards hw registers and orig table */
75 u8 orig[32];
76};
77
78#define to_agp(chip) container_of(chip, struct amd_gpio, chip)
79
80static int amd_gpio_request(struct gpio_chip *chip, unsigned offset)
81{
82 struct amd_gpio *agp = to_agp(chip);
83
84 agp->orig[offset] = ioread8(agp->pm + AMD_REG_GPIO(offset)) &
85 (AMD_GPIO_DEBOUNCE | AMD_GPIO_MODE_MASK | AMD_GPIO_X_MASK);
86
87 dev_dbg(&agp->pdev->dev, "Requested gpio %d, data %x\n", offset, agp->orig[offset]);
88
89 return 0;
90}
91
92static void amd_gpio_free(struct gpio_chip *chip, unsigned offset)
93{
94 struct amd_gpio *agp = to_agp(chip);
95
96 dev_dbg(&agp->pdev->dev, "Freed gpio %d, data %x\n", offset, agp->orig[offset]);
97
98 iowrite8(agp->orig[offset], agp->pm + AMD_REG_GPIO(offset));
99}
100
101static void amd_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
102{
103 struct amd_gpio *agp = to_agp(chip);
104 u8 temp;
105 unsigned long flags;
106
107 spin_lock_irqsave(&agp->lock, flags);
108 temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
109 temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_OUT | (value ? AMD_GPIO_X_OUT_HI : AMD_GPIO_X_OUT_LOW);
110 iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
111 spin_unlock_irqrestore(&agp->lock, flags);
112
113 dev_dbg(&agp->pdev->dev, "Setting gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
114}
115
116static int amd_gpio_get(struct gpio_chip *chip, unsigned offset)
117{
118 struct amd_gpio *agp = to_agp(chip);
119 u8 temp;
120
121 temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
122
123 dev_dbg(&agp->pdev->dev, "Getting gpio %d, reg=%02x\n", offset, temp);
124
125 return (temp & AMD_GPIO_RTIN) ? 1 : 0;
126}
127
128static int amd_gpio_dirout(struct gpio_chip *chip, unsigned offset, int value)
129{
130 struct amd_gpio *agp = to_agp(chip);
131 u8 temp;
132 unsigned long flags;
133
134 spin_lock_irqsave(&agp->lock, flags);
135 temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
136 temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_OUT | (value ? AMD_GPIO_X_OUT_HI : AMD_GPIO_X_OUT_LOW);
137 iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
138 spin_unlock_irqrestore(&agp->lock, flags);
139
140 dev_dbg(&agp->pdev->dev, "Dirout gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
141
142 return 0;
143}
144
145static int amd_gpio_dirin(struct gpio_chip *chip, unsigned offset)
146{
147 struct amd_gpio *agp = to_agp(chip);
148 u8 temp;
149 unsigned long flags;
150
151 spin_lock_irqsave(&agp->lock, flags);
152 temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
153 temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_IN;
154 iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
155 spin_unlock_irqrestore(&agp->lock, flags);
156
157 dev_dbg(&agp->pdev->dev, "Dirin gpio %d, reg=%02x\n", offset, temp);
158
159 return 0;
160}
161
162static struct amd_gpio gp = {
163 .chip = {
164 .label = "AMD GPIO",
165 .owner = THIS_MODULE,
166 .base = -1,
167 .ngpio = 32,
168 .request = amd_gpio_request,
169 .free = amd_gpio_free,
170 .set = amd_gpio_set,
171 .get = amd_gpio_get,
172 .direction_output = amd_gpio_dirout,
173 .direction_input = amd_gpio_dirin,
174 },
175};
176
177static int __init amd_gpio_init(void)
178{
179 int err = -ENODEV;
180 struct pci_dev *pdev = NULL;
181 const struct pci_device_id *ent;
182
183
184 /* We look for our device - AMD South Bridge
185 * I don't know about a system with two such bridges,
186 * so we can assume that there is max. one device.
187 *
188 * We can't use plain pci_driver mechanism,
189 * as the device is really a multiple function device,
190 * main driver that binds to the pci_device is an smbus
191 * driver and have to find & bind to the device this way.
192 */
193 for_each_pci_dev(pdev) {
194 ent = pci_match_id(pci_tbl, pdev);
195 if (ent)
196 goto found;
197 }
198 /* Device not found. */
199 goto out;
200
201found:
202 err = pci_read_config_dword(pdev, 0x58, &gp.pmbase);
203 if (err)
204 goto out;
205 err = -EIO;
206 gp.pmbase &= 0x0000FF00;
207 if (gp.pmbase == 0)
208 goto out;
209 if (!request_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD GPIO")) {
210 dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
211 gp.pmbase + PMBASE_OFFSET);
212 err = -EBUSY;
213 goto out;
214 }
215 gp.pm = ioport_map(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
216 gp.pdev = pdev;
217 gp.chip.dev = &pdev->dev;
218
219 spin_lock_init(&gp.lock);
220
221 printk(KERN_INFO "AMD-8111 GPIO detected\n");
222 err = gpiochip_add(&gp.chip);
223 if (err) {
224 printk(KERN_ERR "GPIO registering failed (%d)\n",
225 err);
226 release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
227 goto out;
228 }
229out:
230 return err;
231}
232
233static void __exit amd_gpio_exit(void)
234{
235 int err = gpiochip_remove(&gp.chip);
236 WARN_ON(err);
237 ioport_unmap(gp.pm);
238 release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
239}
240
241module_init(amd_gpio_init);
242module_exit(amd_gpio_exit);
243
244MODULE_AUTHOR("The Linux Kernel team");
245MODULE_DESCRIPTION("GPIO driver for AMD chipsets");
246MODULE_LICENSE("GPL");