diff options
author | Florian Fainelli <florian@openwrt.org> | 2010-03-20 20:06:09 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2010-05-27 19:37:29 -0400 |
commit | 9956d02d6e60f0c50632ba5699bc6238defb496b (patch) | |
tree | 98a9e77d1a3d58431bf8b4458fa16a4b7c9f3b9c /drivers | |
parent | e090d506c3f1b314059fb77b177cd4193bb81d6e (diff) |
gpio: Add support for RDC321x GPIO controller
This patch adds a new GPIO driver for the RDC321x SoC GPIO controller.
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpio/rdc321x-gpio.c | 245 |
3 files changed, 255 insertions, 1 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4fd0f276df5a..56eb0c59c512 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -282,6 +282,14 @@ config GPIO_TIMBERDALE | |||
282 | ---help--- | 282 | ---help--- |
283 | Add support for the GPIO IP in the timberdale FPGA. | 283 | Add support for the GPIO IP in the timberdale FPGA. |
284 | 284 | ||
285 | config GPIO_RDC321X | ||
286 | tristate "RDC R-321x GPIO support" | ||
287 | depends on PCI && GPIOLIB | ||
288 | select MFD_RDC321X | ||
289 | help | ||
290 | Support for the RDC R321x SoC GPIOs over southbridge | ||
291 | PCI configuration space. | ||
292 | |||
285 | comment "SPI GPIO expanders:" | 293 | comment "SPI GPIO expanders:" |
286 | 294 | ||
287 | config GPIO_MAX7301 | 295 | config GPIO_MAX7301 |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 10f3f8d958b1..d3226d395a90 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -27,4 +27,5 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o | |||
27 | obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o | 27 | obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o |
28 | obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o | 28 | obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o |
29 | obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o | 29 | obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o |
30 | obj-$(CONFIG_GPIO_SCH) += sch_gpio.o \ No newline at end of file | 30 | obj-$(CONFIG_GPIO_SCH) += sch_gpio.o |
31 | obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o | ||
diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c new file mode 100644 index 000000000000..46c55193b9db --- /dev/null +++ b/drivers/gpio/rdc321x-gpio.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * RDC321x GPIO driver | ||
3 | * | ||
4 | * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> | ||
5 | * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | * | ||
21 | */ | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/mfd/rdc321x.h> | ||
30 | |||
31 | struct rdc321x_gpio { | ||
32 | spinlock_t lock; | ||
33 | struct pci_dev *sb_pdev; | ||
34 | u32 data_reg[2]; | ||
35 | int reg1_ctrl_base; | ||
36 | int reg1_data_base; | ||
37 | int reg2_ctrl_base; | ||
38 | int reg2_data_base; | ||
39 | struct gpio_chip chip; | ||
40 | }; | ||
41 | |||
42 | /* read GPIO pin */ | ||
43 | static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) | ||
44 | { | ||
45 | struct rdc321x_gpio *gpch; | ||
46 | u32 value = 0; | ||
47 | int reg; | ||
48 | |||
49 | gpch = container_of(chip, struct rdc321x_gpio, chip); | ||
50 | reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; | ||
51 | |||
52 | spin_lock(&gpch->lock); | ||
53 | pci_write_config_dword(gpch->sb_pdev, reg, | ||
54 | gpch->data_reg[gpio < 32 ? 0 : 1]); | ||
55 | pci_read_config_dword(gpch->sb_pdev, reg, &value); | ||
56 | spin_unlock(&gpch->lock); | ||
57 | |||
58 | return (1 << (gpio & 0x1f)) & value ? 1 : 0; | ||
59 | } | ||
60 | |||
61 | static void rdc_gpio_set_value_impl(struct gpio_chip *chip, | ||
62 | unsigned gpio, int value) | ||
63 | { | ||
64 | struct rdc321x_gpio *gpch; | ||
65 | int reg = (gpio < 32) ? 0 : 1; | ||
66 | |||
67 | gpch = container_of(chip, struct rdc321x_gpio, chip); | ||
68 | |||
69 | if (value) | ||
70 | gpch->data_reg[reg] |= 1 << (gpio & 0x1f); | ||
71 | else | ||
72 | gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); | ||
73 | |||
74 | pci_write_config_dword(gpch->sb_pdev, | ||
75 | reg ? gpch->reg1_data_base : gpch->reg2_data_base, | ||
76 | gpch->data_reg[reg]); | ||
77 | } | ||
78 | |||
79 | /* set GPIO pin to value */ | ||
80 | static void rdc_gpio_set_value(struct gpio_chip *chip, | ||
81 | unsigned gpio, int value) | ||
82 | { | ||
83 | struct rdc321x_gpio *gpch; | ||
84 | |||
85 | gpch = container_of(chip, struct rdc321x_gpio, chip); | ||
86 | spin_lock(&gpch->lock); | ||
87 | rdc_gpio_set_value_impl(chip, gpio, value); | ||
88 | spin_unlock(&gpch->lock); | ||
89 | } | ||
90 | |||
91 | static int rdc_gpio_config(struct gpio_chip *chip, | ||
92 | unsigned gpio, int value) | ||
93 | { | ||
94 | struct rdc321x_gpio *gpch; | ||
95 | int err; | ||
96 | u32 reg; | ||
97 | |||
98 | gpch = container_of(chip, struct rdc321x_gpio, chip); | ||
99 | |||
100 | spin_lock(&gpch->lock); | ||
101 | err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? | ||
102 | gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); | ||
103 | if (err) | ||
104 | goto unlock; | ||
105 | |||
106 | reg |= 1 << (gpio & 0x1f); | ||
107 | |||
108 | err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? | ||
109 | gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); | ||
110 | if (err) | ||
111 | goto unlock; | ||
112 | |||
113 | rdc_gpio_set_value_impl(chip, gpio, value); | ||
114 | |||
115 | unlock: | ||
116 | spin_unlock(&gpch->lock); | ||
117 | |||
118 | return err; | ||
119 | } | ||
120 | |||
121 | /* configure GPIO pin as input */ | ||
122 | static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | ||
123 | { | ||
124 | return rdc_gpio_config(chip, gpio, 1); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Cache the initial value of both GPIO data registers | ||
129 | */ | ||
130 | static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) | ||
131 | { | ||
132 | int err; | ||
133 | struct resource *r; | ||
134 | struct rdc321x_gpio *rdc321x_gpio_dev; | ||
135 | struct rdc321x_gpio_pdata *pdata; | ||
136 | |||
137 | pdata = pdev->dev.platform_data; | ||
138 | if (!pdata) { | ||
139 | dev_err(&pdev->dev, "no platform data supplied\n"); | ||
140 | return -ENODEV; | ||
141 | } | ||
142 | |||
143 | rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL); | ||
144 | if (!rdc321x_gpio_dev) { | ||
145 | dev_err(&pdev->dev, "failed to allocate private data\n"); | ||
146 | return -ENOMEM; | ||
147 | } | ||
148 | |||
149 | r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio-reg1"); | ||
150 | if (!r) { | ||
151 | dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); | ||
152 | err = -ENODEV; | ||
153 | goto out_free; | ||
154 | } | ||
155 | |||
156 | spin_lock_init(&rdc321x_gpio_dev->lock); | ||
157 | rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; | ||
158 | rdc321x_gpio_dev->reg1_ctrl_base = r->start; | ||
159 | rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; | ||
160 | |||
161 | r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio-reg2"); | ||
162 | if (!r) { | ||
163 | dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); | ||
164 | err = -ENODEV; | ||
165 | goto out_free; | ||
166 | } | ||
167 | |||
168 | rdc321x_gpio_dev->reg2_ctrl_base = r->start; | ||
169 | rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; | ||
170 | |||
171 | rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; | ||
172 | rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; | ||
173 | rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; | ||
174 | rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; | ||
175 | rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; | ||
176 | rdc321x_gpio_dev->chip.base = 0; | ||
177 | rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; | ||
178 | |||
179 | platform_set_drvdata(pdev, rdc321x_gpio_dev); | ||
180 | |||
181 | /* This might not be, what others (BIOS, bootloader, etc.) | ||
182 | wrote to these registers before, but it's a good guess. Still | ||
183 | better than just using 0xffffffff. */ | ||
184 | err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, | ||
185 | rdc321x_gpio_dev->reg1_data_base, | ||
186 | &rdc321x_gpio_dev->data_reg[0]); | ||
187 | if (err) | ||
188 | goto out_drvdata; | ||
189 | |||
190 | err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, | ||
191 | rdc321x_gpio_dev->reg2_data_base, | ||
192 | &rdc321x_gpio_dev->data_reg[1]); | ||
193 | if (err) | ||
194 | goto out_drvdata; | ||
195 | |||
196 | dev_info(&pdev->dev, "registering %d GPIOs\n", | ||
197 | rdc321x_gpio_dev->chip.ngpio); | ||
198 | return gpiochip_add(&rdc321x_gpio_dev->chip); | ||
199 | |||
200 | out_drvdata: | ||
201 | platform_set_drvdata(pdev, NULL); | ||
202 | out_free: | ||
203 | kfree(rdc321x_gpio_dev); | ||
204 | return err; | ||
205 | } | ||
206 | |||
207 | static int __devexit rdc321x_gpio_remove(struct platform_device *pdev) | ||
208 | { | ||
209 | int ret; | ||
210 | struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev); | ||
211 | |||
212 | ret = gpiochip_remove(&rdc321x_gpio_dev->chip); | ||
213 | if (ret) | ||
214 | dev_err(&pdev->dev, "failed to unregister chip\n"); | ||
215 | |||
216 | kfree(rdc321x_gpio_dev); | ||
217 | platform_set_drvdata(pdev, NULL); | ||
218 | |||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static struct platform_driver rdc321x_gpio_driver = { | ||
223 | .driver.name = "rdc321x-gpio", | ||
224 | .driver.owner = THIS_MODULE, | ||
225 | .probe = rdc321x_gpio_probe, | ||
226 | .remove = __devexit_p(rdc321x_gpio_remove), | ||
227 | }; | ||
228 | |||
229 | static int __init rdc321x_gpio_init(void) | ||
230 | { | ||
231 | return platform_driver_register(&rdc321x_gpio_driver); | ||
232 | } | ||
233 | |||
234 | static void __exit rdc321x_gpio_exit(void) | ||
235 | { | ||
236 | platform_driver_unregister(&rdc321x_gpio_driver); | ||
237 | } | ||
238 | |||
239 | module_init(rdc321x_gpio_init); | ||
240 | module_exit(rdc321x_gpio_exit); | ||
241 | |||
242 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); | ||
243 | MODULE_DESCRIPTION("RDC321x GPIO driver"); | ||
244 | MODULE_LICENSE("GPL"); | ||
245 | MODULE_ALIAS("platform:rdc321x-gpio"); | ||