1/*
2 * vf610 GPIO support through PORT and GPIO module
3 *
4 * Copyright (c) 2014 Toradex AG.
5 *
6 * Author: Stefan Agner <stefan@agner.ch>.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
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
18#include <linux/bitops.h>
19#include <linux/err.h>
20#include <linux/gpio.h>
21#include <linux/init.h>
22#include <linux/interrupt.h>
23#include <linux/io.h>
24#include <linux/ioport.h>
25#include <linux/irq.h>
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/of.h>
29#include <linux/of_device.h>
30#include <linux/of_irq.h>
31
32#define VF610_GPIO_PER_PORT		32
33
34struct vf610_gpio_port {
35	struct gpio_chip gc;
36	void __iomem *base;
37	void __iomem *gpio_base;
38	u8 irqc[VF610_GPIO_PER_PORT];
39	int irq;
40};
41
42#define GPIO_PDOR		0x00
43#define GPIO_PSOR		0x04
44#define GPIO_PCOR		0x08
45#define GPIO_PTOR		0x0c
46#define GPIO_PDIR		0x10
47
48#define PORT_PCR(n)		((n) * 0x4)
49#define PORT_PCR_IRQC_OFFSET	16
50
51#define PORT_ISFR		0xa0
52#define PORT_DFER		0xc0
53#define PORT_DFCR		0xc4
54#define PORT_DFWR		0xc8
55
56#define PORT_INT_OFF		0x0
57#define PORT_INT_LOGIC_ZERO	0x8
58#define PORT_INT_RISING_EDGE	0x9
59#define PORT_INT_FALLING_EDGE	0xa
60#define PORT_INT_EITHER_EDGE	0xb
61#define PORT_INT_LOGIC_ONE	0xc
62
63static const struct of_device_id vf610_gpio_dt_ids[] = {
64	{ .compatible = "fsl,vf610-gpio" },
65	{ /* sentinel */ }
66};
67
68static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
69{
70	writel_relaxed(val, reg);
71}
72
73static inline u32 vf610_gpio_readl(void __iomem *reg)
74{
75	return readl_relaxed(reg);
76}
77
78static int vf610_gpio_request(struct gpio_chip *chip, unsigned offset)
79{
80	return pinctrl_request_gpio(chip->base + offset);
81}
82
83static void vf610_gpio_free(struct gpio_chip *chip, unsigned offset)
84{
85	pinctrl_free_gpio(chip->base + offset);
86}
87
88static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
89{
90	struct vf610_gpio_port *port =
91		container_of(gc, struct vf610_gpio_port, gc);
92
93	return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) & BIT(gpio));
94}
95
96static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
97{
98	struct vf610_gpio_port *port =
99		container_of(gc, struct vf610_gpio_port, gc);
100	unsigned long mask = BIT(gpio);
101
102	if (val)
103		vf610_gpio_writel(mask, port->gpio_base + GPIO_PSOR);
104	else
105		vf610_gpio_writel(mask, port->gpio_base + GPIO_PCOR);
106}
107
108static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
109{
110	return pinctrl_gpio_direction_input(chip->base + gpio);
111}
112
113static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
114				       int value)
115{
116	vf610_gpio_set(chip, gpio, value);
117
118	return pinctrl_gpio_direction_output(chip->base + gpio);
119}
120
121static void vf610_gpio_irq_handler(u32 irq, struct irq_desc *desc)
122{
123	struct vf610_gpio_port *port = irq_get_handler_data(irq);
124	struct irq_chip *chip = irq_desc_get_chip(desc);
125	int pin;
126	unsigned long irq_isfr;
127
128	chained_irq_enter(chip, desc);
129
130	irq_isfr = vf610_gpio_readl(port->base + PORT_ISFR);
131
132	for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {
133		vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);
134
135		generic_handle_irq(irq_find_mapping(port->gc.irqdomain, pin));
136	}
137
138	chained_irq_exit(chip, desc);
139}
140
141static void vf610_gpio_irq_ack(struct irq_data *d)
142{
143	struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
144	int gpio = d->hwirq;
145
146	vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);
147}
148
149static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
150{
151	struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
152	u8 irqc;
153
154	switch (type) {
155	case IRQ_TYPE_EDGE_RISING:
156		irqc = PORT_INT_RISING_EDGE;
157		break;
158	case IRQ_TYPE_EDGE_FALLING:
159		irqc = PORT_INT_FALLING_EDGE;
160		break;
161	case IRQ_TYPE_EDGE_BOTH:
162		irqc = PORT_INT_EITHER_EDGE;
163		break;
164	case IRQ_TYPE_LEVEL_LOW:
165		irqc = PORT_INT_LOGIC_ZERO;
166		break;
167	case IRQ_TYPE_LEVEL_HIGH:
168		irqc = PORT_INT_LOGIC_ONE;
169		break;
170	default:
171		return -EINVAL;
172	}
173
174	port->irqc[d->hwirq] = irqc;
175
176	return 0;
177}
178
179static void vf610_gpio_irq_mask(struct irq_data *d)
180{
181	struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
182	void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
183
184	vf610_gpio_writel(0, pcr_base);
185}
186
187static void vf610_gpio_irq_unmask(struct irq_data *d)
188{
189	struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
190	void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
191
192	vf610_gpio_writel(port->irqc[d->hwirq] << PORT_PCR_IRQC_OFFSET,
193			  pcr_base);
194}
195
196static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
197{
198	struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
199
200	if (enable)
201		enable_irq_wake(port->irq);
202	else
203		disable_irq_wake(port->irq);
204
205	return 0;
206}
207
208static struct irq_chip vf610_gpio_irq_chip = {
209	.name		= "gpio-vf610",
210	.irq_ack	= vf610_gpio_irq_ack,
211	.irq_mask	= vf610_gpio_irq_mask,
212	.irq_unmask	= vf610_gpio_irq_unmask,
213	.irq_set_type	= vf610_gpio_irq_set_type,
214	.irq_set_wake	= vf610_gpio_irq_set_wake,
215};
216
217static int vf610_gpio_probe(struct platform_device *pdev)
218{
219	struct device *dev = &pdev->dev;
220	struct device_node *np = dev->of_node;
221	struct vf610_gpio_port *port;
222	struct resource *iores;
223	struct gpio_chip *gc;
224	int ret;
225
226	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
227	if (!port)
228		return -ENOMEM;
229
230	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
231	port->base = devm_ioremap_resource(dev, iores);
232	if (IS_ERR(port->base))
233		return PTR_ERR(port->base);
234
235	iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
236	port->gpio_base = devm_ioremap_resource(dev, iores);
237	if (IS_ERR(port->gpio_base))
238		return PTR_ERR(port->gpio_base);
239
240	port->irq = platform_get_irq(pdev, 0);
241	if (port->irq < 0)
242		return port->irq;
243
244	gc = &port->gc;
245	gc->of_node = np;
246	gc->dev = dev;
247	gc->label = "vf610-gpio";
248	gc->ngpio = VF610_GPIO_PER_PORT;
249	gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
250
251	gc->request = vf610_gpio_request;
252	gc->free = vf610_gpio_free;
253	gc->direction_input = vf610_gpio_direction_input;
254	gc->get = vf610_gpio_get;
255	gc->direction_output = vf610_gpio_direction_output;
256	gc->set = vf610_gpio_set;
257
258	ret = gpiochip_add(gc);
259	if (ret < 0)
260		return ret;
261
262	/* Clear the interrupt status register for all GPIO's */
263	vf610_gpio_writel(~0, port->base + PORT_ISFR);
264
265	ret = gpiochip_irqchip_add(gc, &vf610_gpio_irq_chip, 0,
266				   handle_simple_irq, IRQ_TYPE_NONE);
267	if (ret) {
268		dev_err(dev, "failed to add irqchip\n");
269		gpiochip_remove(gc);
270		return ret;
271	}
272	gpiochip_set_chained_irqchip(gc, &vf610_gpio_irq_chip, port->irq,
273				     vf610_gpio_irq_handler);
274
275	return 0;
276}
277
278static struct platform_driver vf610_gpio_driver = {
279	.driver		= {
280		.name	= "gpio-vf610",
281		.of_match_table = vf610_gpio_dt_ids,
282	},
283	.probe		= vf610_gpio_probe,
284};
285
286static int __init gpio_vf610_init(void)
287{
288	return platform_driver_register(&vf610_gpio_driver);
289}
290device_initcall(gpio_vf610_init);
291
292MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
293MODULE_DESCRIPTION("Freescale VF610 GPIO");
294MODULE_LICENSE("GPL v2");
295