aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2013-06-24 00:00:05 -0400
committerLinus Walleij <linus.walleij@linaro.org>2013-07-21 10:30:19 -0400
commitd22fcde0b5409a946567387dfdde79a07843bb78 (patch)
tree594165efc499112178017d2898e7b9b71794398b
parenta3d88c92a1d7ebbe1e956fd9f0f37bc383f2571b (diff)
gpio: Kontron PLD gpio driver
Add gpio support for the on-board PLD found on some Kontron embedded modules. Originally-from: Michael Brunner <michael.brunner@kontron.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Kevin Strasser <kevin.strasser@linux.intel.com> Acked-by: Darren Hart <dvhart@linux.intel.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/gpio/Kconfig12
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-kempld.c225
3 files changed, 238 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 02db4eafa9df..4b7ba53e96db 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -691,6 +691,18 @@ config GPIO_UCB1400
691 This enables support for the Philips UCB1400 GPIO pins. 691 This enables support for the Philips UCB1400 GPIO pins.
692 The UCB1400 is an AC97 audio codec. 692 The UCB1400 is an AC97 audio codec.
693 693
694comment "LPC GPIO expanders:"
695
696config GPIO_KEMPLD
697 tristate "Kontron ETX / COMexpress GPIO"
698 depends on MFD_KEMPLD
699 help
700 This enables support for the PLD GPIO interface on some Kontron ETX
701 and COMexpress (ETXexpress) modules.
702
703 This driver can also be built as a module. If so, the module will be
704 called gpio-kempld.
705
694comment "MODULbus GPIO expanders:" 706comment "MODULbus GPIO expanders:"
695 707
696config GPIO_JANZ_TTL 708config GPIO_JANZ_TTL
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fa97bf8698ed..156fd283945c 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
29obj-$(CONFIG_GPIO_ICH) += gpio-ich.o 29obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
30obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o 30obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
31obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o 31obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
32obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
32obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o 33obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
33obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o 34obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o
34obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o 35obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
new file mode 100644
index 000000000000..1bdc3a2e40dc
--- /dev/null
+++ b/drivers/gpio/gpio-kempld.c
@@ -0,0 +1,225 @@
1/*
2 * Kontron PLD GPIO driver
3 *
4 * Copyright (c) 2010-2013 Kontron Europe GmbH
5 * Author: Michael Brunner <michael.brunner@kontron.com>
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 2 as published
9 * by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/bitops.h>
21#include <linux/errno.h>
22#include <linux/platform_device.h>
23#include <linux/gpio.h>
24#include <linux/mfd/kempld.h>
25
26#define KEMPLD_GPIO_MAX_NUM 16
27#define KEMPLD_GPIO_MASK(x) (1 << ((x) % 8))
28#define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8)
29#define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8)
30#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
31#define KEMPLD_GPIO_IEN 0x4A
32
33struct kempld_gpio_data {
34 struct gpio_chip chip;
35 struct kempld_device_data *pld;
36};
37
38/*
39 * Set or clear GPIO bit
40 * kempld_get_mutex must be called prior to calling this function.
41 */
42static void kempld_gpio_bitop(struct kempld_device_data *pld,
43 u8 reg, u8 bit, u8 val)
44{
45 u8 status;
46
47 status = kempld_read8(pld, reg);
48 if (val)
49 status |= (1 << bit);
50 else
51 status &= ~(1 << bit);
52 kempld_write8(pld, reg, status);
53}
54
55static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit)
56{
57 u8 status;
58
59 kempld_get_mutex(pld);
60 status = kempld_read8(pld, reg);
61 kempld_release_mutex(pld);
62
63 return !!(status & (1 << bit));
64}
65
66static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset)
67{
68 struct kempld_gpio_data *gpio
69 = container_of(chip, struct kempld_gpio_data, chip);
70 struct kempld_device_data *pld = gpio->pld;
71
72 return kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset),
73 KEMPLD_GPIO_MASK(offset));
74}
75
76static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
77{
78 struct kempld_gpio_data *gpio
79 = container_of(chip, struct kempld_gpio_data, chip);
80 struct kempld_device_data *pld = gpio->pld;
81
82 kempld_get_mutex(pld);
83 kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset),
84 KEMPLD_GPIO_MASK(offset), value);
85 kempld_release_mutex(pld);
86}
87
88static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
89{
90 struct kempld_gpio_data *gpio
91 = container_of(chip, struct kempld_gpio_data, chip);
92 struct kempld_device_data *pld = gpio->pld;
93
94 kempld_get_mutex(pld);
95 kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset),
96 KEMPLD_GPIO_MASK(offset), 0);
97 kempld_release_mutex(pld);
98
99 return 0;
100}
101
102static int kempld_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
103 int value)
104{
105 struct kempld_gpio_data *gpio
106 = container_of(chip, struct kempld_gpio_data, chip);
107 struct kempld_device_data *pld = gpio->pld;
108
109 kempld_get_mutex(pld);
110 kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset),
111 KEMPLD_GPIO_MASK(offset), value);
112 kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset),
113 KEMPLD_GPIO_MASK(offset), 1);
114 kempld_release_mutex(pld);
115
116 return 0;
117}
118
119static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
120{
121 struct kempld_gpio_data *gpio
122 = container_of(chip, struct kempld_gpio_data, chip);
123 struct kempld_device_data *pld = gpio->pld;
124
125 return kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset),
126 KEMPLD_GPIO_MASK(offset));
127}
128
129static int kempld_gpio_pincount(struct kempld_device_data *pld)
130{
131 u16 evt, evt_back;
132
133 kempld_get_mutex(pld);
134
135 /* Backup event register as it might be already initialized */
136 evt_back = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
137 /* Clear event register */
138 kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, 0x0000);
139 /* Read back event register */
140 evt = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
141 /* Restore event register */
142 kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, evt_back);
143
144 kempld_release_mutex(pld);
145
146 return evt ? __ffs(evt) : 16;
147}
148
149static int kempld_gpio_probe(struct platform_device *pdev)
150{
151 struct device *dev = &pdev->dev;
152 struct kempld_device_data *pld = dev_get_drvdata(dev->parent);
153 struct kempld_platform_data *pdata = pld->dev->platform_data;
154 struct kempld_gpio_data *gpio;
155 struct gpio_chip *chip;
156 int ret;
157
158 if (pld->info.spec_major < 2) {
159 dev_err(dev,
160 "Driver only supports GPIO devices compatible to PLD spec. rev. 2.0 or higher\n");
161 return -ENODEV;
162 }
163
164 gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
165 if (gpio == NULL)
166 return -ENOMEM;
167
168 gpio->pld = pld;
169
170 platform_set_drvdata(pdev, gpio);
171
172 chip = &gpio->chip;
173 chip->label = "gpio-kempld";
174 chip->owner = THIS_MODULE;
175 chip->dev = dev;
176 chip->can_sleep = 1;
177 if (pdata && pdata->gpio_base)
178 chip->base = pdata->gpio_base;
179 else
180 chip->base = -1;
181 chip->direction_input = kempld_gpio_direction_input;
182 chip->direction_output = kempld_gpio_direction_output;
183 chip->get_direction = kempld_gpio_get_direction;
184 chip->get = kempld_gpio_get;
185 chip->set = kempld_gpio_set;
186 chip->ngpio = kempld_gpio_pincount(pld);
187 if (chip->ngpio == 0) {
188 dev_err(dev, "No GPIO pins detected\n");
189 return -ENODEV;
190 }
191
192 ret = gpiochip_add(chip);
193 if (ret) {
194 dev_err(dev, "Could not register GPIO chip\n");
195 return ret;
196 }
197
198 dev_info(dev, "GPIO functionality initialized with %d pins\n",
199 chip->ngpio);
200
201 return 0;
202}
203
204static int kempld_gpio_remove(struct platform_device *pdev)
205{
206 struct kempld_gpio_data *gpio = platform_get_drvdata(pdev);
207
208 return gpiochip_remove(&gpio->chip);
209}
210
211static struct platform_driver kempld_gpio_driver = {
212 .driver = {
213 .name = "gpio-kempld",
214 .owner = THIS_MODULE,
215 },
216 .probe = kempld_gpio_probe,
217 .remove = kempld_gpio_remove,
218};
219
220module_platform_driver(kempld_gpio_driver);
221
222MODULE_DESCRIPTION("KEM PLD GPIO Driver");
223MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
224MODULE_LICENSE("GPL");
225MODULE_ALIAS("platform:gpio-kempld");