aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Drake <dsd@laptop.org>2010-09-30 16:55:48 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2010-10-28 18:29:51 -0400
commit99ea2626a75e13ce926af69d96e9ae5bfb62b7ba (patch)
tree1cbfaaf0ff0c1ddb38fd0909e9e969aa2cc559b2
parent375fc77ba950e388f29d11cb3279ada5e5c1580c (diff)
gpio: Add VIA VX855 GPIO driver
This is needed for supporting the upcoming VX855 camera and OLPC DCON drivers, as well as the advanced viafb features on non-OLPC hardware based on this chip. Based on earlier work by Harald Welte. Signed-off-by: Daniel Drake <dsd@laptop.org> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/gpio/Kconfig12
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/vx855_gpio.c332
3 files changed, 345 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index dd9b4ba8d32..3143ac795eb 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -116,6 +116,18 @@ config GPIO_SCH
116 This driver can also be built as a module. If so, the module 116 This driver can also be built as a module. If so, the module
117 will be called sch-gpio. 117 will be called sch-gpio.
118 118
119config GPIO_VX855
120 tristate "VIA VX855/VX875 GPIO"
121 depends on GPIOLIB
122 select MFD_CORE
123 select MFD_VX855
124 help
125 Support access to the VX855/VX875 GPIO lines through the gpio library.
126
127 This driver provides common support for accessing the device,
128 additional drivers must be enabled in order to use the
129 functionality of the device.
130
119comment "I2C GPIO expanders:" 131comment "I2C GPIO expanders:"
120 132
121config GPIO_MAX7300 133config GPIO_MAX7300
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index da2ecde5abd..bdf3ddec065 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -40,3 +40,4 @@ obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
40obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o 40obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
41obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o 41obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
42obj-$(CONFIG_GPIO_SX150X) += sx150x.o 42obj-$(CONFIG_GPIO_SX150X) += sx150x.o
43obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
diff --git a/drivers/gpio/vx855_gpio.c b/drivers/gpio/vx855_gpio.c
new file mode 100644
index 00000000000..8a98ee5d5f6
--- /dev/null
+++ b/drivers/gpio/vx855_gpio.c
@@ -0,0 +1,332 @@
1/*
2 * Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO
3 *
4 * Copyright (C) 2009 VIA Technologies, Inc.
5 * Copyright (C) 2010 One Laptop per Child
6 * Author: Harald Welte <HaraldWelte@viatech.com>
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22 * MA 02111-1307 USA
23 *
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/gpio.h>
29#include <linux/device.h>
30#include <linux/platform_device.h>
31#include <linux/pci.h>
32#include <linux/io.h>
33
34#define MODULE_NAME "vx855_gpio"
35
36/* The VX855 south bridge has the following GPIO pins:
37 * GPI 0...13 General Purpose Input
38 * GPO 0...12 General Purpose Output
39 * GPIO 0...14 General Purpose I/O (Open-Drain)
40 */
41
42#define NR_VX855_GPI 14
43#define NR_VX855_GPO 13
44#define NR_VX855_GPIO 15
45
46#define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO)
47#define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO)
48
49struct vx855_gpio {
50 struct gpio_chip gpio;
51 spinlock_t lock;
52 u32 io_gpi;
53 u32 io_gpo;
54 bool gpi_reserved;
55 bool gpo_reserved;
56};
57
58/* resolve a GPIx into the corresponding bit position */
59static inline u_int32_t gpi_i_bit(int i)
60{
61 if (i < 10)
62 return 1 << i;
63 else
64 return 1 << (i + 14);
65}
66
67static inline u_int32_t gpo_o_bit(int i)
68{
69 if (i < 11)
70 return 1 << i;
71 else
72 return 1 << (i + 14);
73}
74
75static inline u_int32_t gpio_i_bit(int i)
76{
77 if (i < 14)
78 return 1 << (i + 10);
79 else
80 return 1 << (i + 14);
81}
82
83static inline u_int32_t gpio_o_bit(int i)
84{
85 if (i < 14)
86 return 1 << (i + 11);
87 else
88 return 1 << (i + 13);
89}
90
91/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering:
92 * 0..13 GPI 0..13
93 * 14..26 GPO 0..12
94 * 27..41 GPIO 0..14
95 */
96
97static int vx855gpio_direction_input(struct gpio_chip *gpio,
98 unsigned int nr)
99{
100 struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
101 unsigned long flags;
102 u_int32_t reg_out;
103
104 /* Real GPI bits are always in input direction */
105 if (nr < NR_VX855_GPI)
106 return 0;
107
108 /* Real GPO bits cannot be put in output direction */
109 if (nr < NR_VX855_GPInO)
110 return -EINVAL;
111
112 /* Open Drain GPIO have to be set to one */
113 spin_lock_irqsave(&vg->lock, flags);
114 reg_out = inl(vg->io_gpo);
115 reg_out |= gpio_o_bit(nr - NR_VX855_GPInO);
116 outl(reg_out, vg->io_gpo);
117 spin_unlock_irqrestore(&vg->lock, flags);
118
119 return 0;
120}
121
122static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr)
123{
124 struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
125 u_int32_t reg_in;
126 int ret = 0;
127
128 if (nr < NR_VX855_GPI) {
129 reg_in = inl(vg->io_gpi);
130 if (reg_in & gpi_i_bit(nr))
131 ret = 1;
132 } else if (nr < NR_VX855_GPInO) {
133 /* GPO don't have an input bit, we need to read it
134 * back from the output register */
135 reg_in = inl(vg->io_gpo);
136 if (reg_in & gpo_o_bit(nr - NR_VX855_GPI))
137 ret = 1;
138 } else {
139 reg_in = inl(vg->io_gpi);
140 if (reg_in & gpio_i_bit(nr - NR_VX855_GPInO))
141 ret = 1;
142 }
143
144 return ret;
145}
146
147static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr,
148 int val)
149{
150 struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
151 unsigned long flags;
152 u_int32_t reg_out;
153
154 /* True GPI cannot be switched to output mode */
155 if (nr < NR_VX855_GPI)
156 return;
157
158 spin_lock_irqsave(&vg->lock, flags);
159 reg_out = inl(vg->io_gpo);
160 if (nr < NR_VX855_GPInO) {
161 if (val)
162 reg_out |= gpo_o_bit(nr - NR_VX855_GPI);
163 else
164 reg_out &= ~gpo_o_bit(nr - NR_VX855_GPI);
165 } else {
166 if (val)
167 reg_out |= gpio_o_bit(nr - NR_VX855_GPInO);
168 else
169 reg_out &= ~gpio_o_bit(nr - NR_VX855_GPInO);
170 }
171 outl(reg_out, vg->io_gpo);
172 spin_unlock_irqrestore(&vg->lock, flags);
173}
174
175static int vx855gpio_direction_output(struct gpio_chip *gpio,
176 unsigned int nr, int val)
177{
178 /* True GPI cannot be switched to output mode */
179 if (nr < NR_VX855_GPI)
180 return -EINVAL;
181
182 /* True GPO don't need to be switched to output mode,
183 * and GPIO are open-drain, i.e. also need no switching,
184 * so all we do is set the level */
185 vx855gpio_set(gpio, nr, val);
186
187 return 0;
188}
189
190static const char *vx855gpio_names[NR_VX855_GP] = {
191 "VX855_GPI0", "VX855_GPI1", "VX855_GPI2", "VX855_GPI3", "VX855_GPI4",
192 "VX855_GPI5", "VX855_GPI6", "VX855_GPI7", "VX855_GPI8", "VX855_GPI9",
193 "VX855_GPI10", "VX855_GPI11", "VX855_GPI12", "VX855_GPI13",
194 "VX855_GPO0", "VX855_GPO1", "VX855_GPO2", "VX855_GPO3", "VX855_GPO4",
195 "VX855_GPO5", "VX855_GPO6", "VX855_GPO7", "VX855_GPO8", "VX855_GPO9",
196 "VX855_GPO10", "VX855_GPO11", "VX855_GPO12",
197 "VX855_GPIO0", "VX855_GPIO1", "VX855_GPIO2", "VX855_GPIO3",
198 "VX855_GPIO4", "VX855_GPIO5", "VX855_GPIO6", "VX855_GPIO7",
199 "VX855_GPIO8", "VX855_GPIO9", "VX855_GPIO10", "VX855_GPIO11",
200 "VX855_GPIO12", "VX855_GPIO13", "VX855_GPIO14"
201};
202
203static void vx855gpio_gpio_setup(struct vx855_gpio *vg)
204{
205 struct gpio_chip *c = &vg->gpio;
206
207 c->label = "VX855 South Bridge";
208 c->owner = THIS_MODULE;
209 c->direction_input = vx855gpio_direction_input;
210 c->direction_output = vx855gpio_direction_output;
211 c->get = vx855gpio_get;
212 c->set = vx855gpio_set;
213 c->dbg_show = NULL;
214 c->base = 0;
215 c->ngpio = NR_VX855_GP;
216 c->can_sleep = 0;
217 c->names = vx855gpio_names;
218}
219
220/* This platform device is ordinarily registered by the vx855 mfd driver */
221static __devinit int vx855gpio_probe(struct platform_device *pdev)
222{
223 struct resource *res_gpi;
224 struct resource *res_gpo;
225 struct vx855_gpio *vg;
226 int ret;
227
228 res_gpi = platform_get_resource(pdev, IORESOURCE_IO, 0);
229 res_gpo = platform_get_resource(pdev, IORESOURCE_IO, 1);
230 if (!res_gpi || !res_gpo)
231 return -EBUSY;
232
233 vg = kzalloc(sizeof(*vg), GFP_KERNEL);
234 if (!vg)
235 return -ENOMEM;
236
237 platform_set_drvdata(pdev, vg);
238
239 dev_info(&pdev->dev, "found VX855 GPIO controller\n");
240 vg->io_gpi = res_gpi->start;
241 vg->io_gpo = res_gpo->start;
242 spin_lock_init(&vg->lock);
243
244 /*
245 * A single byte is used to control various GPIO ports on the VX855,
246 * and in the case of the OLPC XO-1.5, some of those ports are used
247 * for switches that are interpreted and exposed through ACPI. ACPI
248 * will have reserved the region, so our own reservation will not
249 * succeed. Ignore and continue.
250 */
251
252 if (!request_region(res_gpi->start, resource_size(res_gpi),
253 MODULE_NAME "_gpi"))
254 dev_warn(&pdev->dev,
255 "GPI I/O resource busy, probably claimed by ACPI\n");
256 else
257 vg->gpi_reserved = true;
258
259 if (!request_region(res_gpo->start, resource_size(res_gpo),
260 MODULE_NAME "_gpo"))
261 dev_warn(&pdev->dev,
262 "GPO I/O resource busy, probably claimed by ACPI\n");
263 else
264 vg->gpo_reserved = true;
265
266 vx855gpio_gpio_setup(vg);
267
268 ret = gpiochip_add(&vg->gpio);
269 if (ret) {
270 dev_err(&pdev->dev, "failed to register GPIOs\n");
271 goto out_release;
272 }
273
274 return 0;
275
276out_release:
277 if (vg->gpi_reserved)
278 release_region(res_gpi->start, resource_size(res_gpi));
279 if (vg->gpo_reserved)
280 release_region(res_gpi->start, resource_size(res_gpo));
281 platform_set_drvdata(pdev, NULL);
282 kfree(vg);
283 return ret;
284}
285
286static int __devexit vx855gpio_remove(struct platform_device *pdev)
287{
288 struct vx855_gpio *vg = platform_get_drvdata(pdev);
289 struct resource *res;
290
291 if (gpiochip_remove(&vg->gpio))
292 dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
293
294 if (vg->gpi_reserved) {
295 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
296 release_region(res->start, resource_size(res));
297 }
298 if (vg->gpo_reserved) {
299 res = platform_get_resource(pdev, IORESOURCE_IO, 1);
300 release_region(res->start, resource_size(res));
301 }
302
303 platform_set_drvdata(pdev, NULL);
304 kfree(vg);
305 return 0;
306}
307
308static struct platform_driver vx855gpio_driver = {
309 .driver = {
310 .name = MODULE_NAME,
311 .owner = THIS_MODULE,
312 },
313 .probe = vx855gpio_probe,
314 .remove = __devexit_p(vx855gpio_remove),
315};
316
317static int vx855gpio_init(void)
318{
319 return platform_driver_register(&vx855gpio_driver);
320}
321module_init(vx855gpio_init);
322
323static void vx855gpio_exit(void)
324{
325 platform_driver_unregister(&vx855gpio_driver);
326}
327module_exit(vx855gpio_exit);
328
329MODULE_LICENSE("GPL");
330MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
331MODULE_DESCRIPTION("GPIO driver for the VIA VX855 chipset");
332MODULE_ALIAS("platform:vx855_gpio");