Skip to content

Commit b868db9

Browse files
lunnlinusw
authored andcommitted
gpio: tqmx86: Add GPIO from for this IO controller
Some TQ-Systems ComExpress modules contain an IO controller with 8 GPIO lines. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
1 parent 9ce01ef commit b868db9

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed

drivers/gpio/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,13 @@ config GPIO_TPS68470
11911191
of the TPS68470 must be available before dependent
11921192
drivers are loaded.
11931193

1194+
config GPIO_TQMX86
1195+
tristate "TQ-Systems QTMX86 GPIO"
1196+
depends on MFD_TQMX86 || COMPILE_TEST
1197+
select GPIOLIB_IRQCHIP
1198+
help
1199+
This driver supports GPIO on the TQMX86 IO controller.
1200+
11941201
config GPIO_TWL4030
11951202
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
11961203
depends on TWL4030_CORE

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
135135
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
136136
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
137137
obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o
138+
obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o
138139
obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o
139140
obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o
140141
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o

drivers/gpio/gpio-tqmx86.c

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* TQ-Systems TQMx86 PLD GPIO driver
4+
*
5+
* Based on vendor driver by:
6+
* Vadim V.Vlasov <vvlasov@dev.rtsoft.ru>
7+
*/
8+
9+
#include <linux/bitops.h>
10+
#include <linux/errno.h>
11+
#include <linux/gpio/driver.h>
12+
#include <linux/init.h>
13+
#include <linux/interrupt.h>
14+
#include <linux/kernel.h>
15+
#include <linux/module.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/pm_runtime.h>
18+
#include <linux/slab.h>
19+
20+
#define TQMX86_NGPIO 8
21+
#define TQMX86_NGPO 4 /* 0-3 - output */
22+
#define TQMX86_NGPI 4 /* 4-7 - input */
23+
#define TQMX86_DIR_INPUT_MASK 0xf0 /* 0-3 - output, 4-7 - input */
24+
25+
#define TQMX86_GPIODD 0 /* GPIO Data Direction Register */
26+
#define TQMX86_GPIOD 1 /* GPIO Data Register */
27+
#define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */
28+
#define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */
29+
30+
#define TQMX86_GPII_FALLING BIT(0)
31+
#define TQMX86_GPII_RISING BIT(1)
32+
#define TQMX86_GPII_MASK (BIT(0) | BIT(1))
33+
#define TQMX86_GPII_BITS 2
34+
35+
struct tqmx86_gpio_data {
36+
struct gpio_chip chip;
37+
struct irq_chip irq_chip;
38+
void __iomem *io_base;
39+
int irq;
40+
raw_spinlock_t spinlock;
41+
u8 irq_type[TQMX86_NGPI];
42+
};
43+
44+
static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg)
45+
{
46+
return ioread8(gd->io_base + reg);
47+
}
48+
49+
static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val,
50+
unsigned int reg)
51+
{
52+
iowrite8(val, gd->io_base + reg);
53+
}
54+
55+
static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset)
56+
{
57+
struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
58+
59+
return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset));
60+
}
61+
62+
static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset,
63+
int value)
64+
{
65+
struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
66+
unsigned long flags;
67+
u8 val;
68+
69+
raw_spin_lock_irqsave(&gpio->spinlock, flags);
70+
val = tqmx86_gpio_read(gpio, TQMX86_GPIOD);
71+
if (value)
72+
val |= BIT(offset);
73+
else
74+
val &= ~BIT(offset);
75+
tqmx86_gpio_write(gpio, val, TQMX86_GPIOD);
76+
raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
77+
}
78+
79+
static int tqmx86_gpio_direction_input(struct gpio_chip *chip,
80+
unsigned int offset)
81+
{
82+
/* Direction cannot be changed. Validate is an input. */
83+
if (BIT(offset) & TQMX86_DIR_INPUT_MASK)
84+
return 0;
85+
else
86+
return -EINVAL;
87+
}
88+
89+
static int tqmx86_gpio_direction_output(struct gpio_chip *chip,
90+
unsigned int offset,
91+
int value)
92+
{
93+
/* Direction cannot be changed, validate is an output */
94+
if (BIT(offset) & TQMX86_DIR_INPUT_MASK)
95+
return -EINVAL;
96+
else
97+
return 0;
98+
}
99+
100+
static int tqmx86_gpio_get_direction(struct gpio_chip *chip,
101+
unsigned int offset)
102+
{
103+
return !!(TQMX86_DIR_INPUT_MASK & BIT(offset));
104+
}
105+
106+
static void tqmx86_gpio_irq_mask(struct irq_data *data)
107+
{
108+
unsigned int offset = (data->hwirq - TQMX86_NGPO);
109+
struct tqmx86_gpio_data *gpio = gpiochip_get_data(
110+
irq_data_get_irq_chip_data(data));
111+
unsigned long flags;
112+
u8 gpiic, mask;
113+
114+
mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS);
115+
116+
raw_spin_lock_irqsave(&gpio->spinlock, flags);
117+
gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
118+
gpiic &= ~mask;
119+
tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC);
120+
raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
121+
}
122+
123+
static void tqmx86_gpio_irq_unmask(struct irq_data *data)
124+
{
125+
unsigned int offset = (data->hwirq - TQMX86_NGPO);
126+
struct tqmx86_gpio_data *gpio = gpiochip_get_data(
127+
irq_data_get_irq_chip_data(data));
128+
unsigned long flags;
129+
u8 gpiic, mask;
130+
131+
mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS);
132+
133+
raw_spin_lock_irqsave(&gpio->spinlock, flags);
134+
gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
135+
gpiic &= ~mask;
136+
gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS);
137+
tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC);
138+
raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
139+
}
140+
141+
static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type)
142+
{
143+
struct tqmx86_gpio_data *gpio = gpiochip_get_data(
144+
irq_data_get_irq_chip_data(data));
145+
unsigned int offset = (data->hwirq - TQMX86_NGPO);
146+
unsigned int edge_type = type & IRQF_TRIGGER_MASK;
147+
unsigned long flags;
148+
u8 new_type, gpiic;
149+
150+
switch (edge_type) {
151+
case IRQ_TYPE_EDGE_RISING:
152+
new_type = TQMX86_GPII_RISING;
153+
break;
154+
case IRQ_TYPE_EDGE_FALLING:
155+
new_type = TQMX86_GPII_FALLING;
156+
break;
157+
case IRQ_TYPE_EDGE_BOTH:
158+
new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING;
159+
break;
160+
default:
161+
return -EINVAL; /* not supported */
162+
}
163+
164+
gpio->irq_type[offset] = new_type;
165+
166+
raw_spin_lock_irqsave(&gpio->spinlock, flags);
167+
gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
168+
gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS));
169+
gpiic |= new_type << (offset * TQMX86_GPII_BITS);
170+
tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC);
171+
raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
172+
173+
return 0;
174+
}
175+
176+
static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
177+
{
178+
struct gpio_chip *chip = irq_desc_get_handler_data(desc);
179+
struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
180+
struct irq_chip *irq_chip = irq_desc_get_chip(desc);
181+
unsigned long irq_bits;
182+
int i = 0, child_irq;
183+
u8 irq_status;
184+
185+
chained_irq_enter(irq_chip, desc);
186+
187+
irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS);
188+
tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);
189+
190+
irq_bits = irq_status;
191+
for_each_set_bit(i, &irq_bits, TQMX86_NGPI) {
192+
child_irq = irq_find_mapping(gpio->chip.irq.domain,
193+
i + TQMX86_NGPO);
194+
generic_handle_irq(child_irq);
195+
}
196+
197+
chained_irq_exit(irq_chip, desc);
198+
}
199+
200+
/* Minimal runtime PM is needed by the IRQ subsystem */
201+
static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev)
202+
{
203+
return 0;
204+
}
205+
206+
static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev)
207+
{
208+
return 0;
209+
}
210+
211+
static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = {
212+
SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend,
213+
tqmx86_gpio_runtime_resume, NULL)
214+
};
215+
216+
static int tqmx86_gpio_probe(struct platform_device *pdev)
217+
{
218+
struct device *dev = &pdev->dev;
219+
struct tqmx86_gpio_data *gpio;
220+
struct gpio_chip *chip;
221+
void __iomem *io_base;
222+
struct resource *res;
223+
int ret, irq;
224+
225+
irq = platform_get_irq(pdev, 0);
226+
if (irq < 0)
227+
return irq;
228+
229+
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
230+
if (!res) {
231+
dev_err(&pdev->dev, "Cannot get I/O\n");
232+
return -ENODEV;
233+
}
234+
235+
io_base = devm_ioport_map(&pdev->dev, res->start, resource_size(res));
236+
if (!io_base)
237+
return -ENOMEM;
238+
239+
gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
240+
if (!gpio)
241+
return -ENOMEM;
242+
243+
raw_spin_lock_init(&gpio->spinlock);
244+
gpio->io_base = io_base;
245+
246+
tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD);
247+
248+
platform_set_drvdata(pdev, gpio);
249+
250+
chip = &gpio->chip;
251+
chip->label = "gpio-tqmx86";
252+
chip->owner = THIS_MODULE;
253+
chip->can_sleep = false;
254+
chip->base = -1;
255+
chip->direction_input = tqmx86_gpio_direction_input;
256+
chip->direction_output = tqmx86_gpio_direction_output;
257+
chip->get_direction = tqmx86_gpio_get_direction;
258+
chip->get = tqmx86_gpio_get;
259+
chip->set = tqmx86_gpio_set;
260+
chip->ngpio = TQMX86_NGPIO;
261+
chip->irq.need_valid_mask = true;
262+
chip->parent = pdev->dev.parent;
263+
264+
pm_runtime_enable(&pdev->dev);
265+
266+
ret = devm_gpiochip_add_data(dev, chip, gpio);
267+
if (ret) {
268+
dev_err(dev, "Could not register GPIO chip\n");
269+
goto out_pm_dis;
270+
}
271+
272+
if (irq) {
273+
struct irq_chip *irq_chip = &gpio->irq_chip;
274+
u8 irq_status;
275+
276+
irq_chip->name = chip->label;
277+
irq_chip->parent_device = &pdev->dev;
278+
irq_chip->irq_mask = tqmx86_gpio_irq_mask;
279+
irq_chip->irq_unmask = tqmx86_gpio_irq_unmask;
280+
irq_chip->irq_set_type = tqmx86_gpio_irq_set_type;
281+
282+
/* Mask all interrupts */
283+
tqmx86_gpio_write(gpio, 0, TQMX86_GPIIC);
284+
285+
/* Clear all pending interrupts */
286+
irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS);
287+
tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);
288+
289+
ret = gpiochip_irqchip_add(chip, irq_chip,
290+
0, handle_simple_irq,
291+
IRQ_TYPE_EDGE_BOTH);
292+
if (ret) {
293+
dev_err(dev, "Could not add irq chip\n");
294+
goto out_remove;
295+
}
296+
297+
gpiochip_set_chained_irqchip(chip, irq_chip,
298+
irq, tqmx86_gpio_irq_handler);
299+
}
300+
301+
/* Only GPIOs 4-7 are valid for interrupts. Clear the others */
302+
clear_bit(0, chip->irq.valid_mask);
303+
clear_bit(1, chip->irq.valid_mask);
304+
clear_bit(2, chip->irq.valid_mask);
305+
clear_bit(3, chip->irq.valid_mask);
306+
307+
dev_info(dev, "GPIO functionality initialized with %d pins\n",
308+
chip->ngpio);
309+
310+
return 0;
311+
312+
out_remove:
313+
gpiochip_remove(&gpio->chip);
314+
out_pm_dis:
315+
pm_runtime_disable(&pdev->dev);
316+
317+
return ret;
318+
}
319+
320+
static struct platform_driver tqmx86_gpio_driver = {
321+
.driver = {
322+
.name = "tqmx86-gpio",
323+
.pm = &tqmx86_gpio_dev_pm_ops,
324+
},
325+
.probe = tqmx86_gpio_probe,
326+
};
327+
328+
module_platform_driver(tqmx86_gpio_driver);
329+
330+
MODULE_DESCRIPTION("TQMx86 PLD GPIO Driver");
331+
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
332+
MODULE_LICENSE("GPL");
333+
MODULE_ALIAS("platform:tqmx86-gpio");

0 commit comments

Comments
 (0)