Blame view

drivers/gpio/gpio-vf610.c 8.52 KB
36e2add18   Linus Walleij   gpio: vf610: Cut ...
1
  // SPDX-License-Identifier: GPL-2.0+
7f2691a19   Stefan Agner   gpio: vf610: add ...
2
  /*
adaaf63e2   Paul Gortmaker   gpio: vf610: drop...
3
   * Freescale vf610 GPIO support through PORT and GPIO
7f2691a19   Stefan Agner   gpio: vf610: add ...
4
5
6
7
   *
   * Copyright (c) 2014 Toradex AG.
   *
   * Author: Stefan Agner <stefan@agner.ch>.
7f2691a19   Stefan Agner   gpio: vf610: add ...
8
   */
7f2691a19   Stefan Agner   gpio: vf610: add ...
9
  #include <linux/bitops.h>
91393622b   A.s. Dong   gpio: vf610: add ...
10
  #include <linux/clk.h>
7f2691a19   Stefan Agner   gpio: vf610: add ...
11
  #include <linux/err.h>
45e8296cc   Linus Walleij   gpio: vf610: Incl...
12
  #include <linux/gpio/driver.h>
7f2691a19   Stefan Agner   gpio: vf610: add ...
13
14
15
16
17
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/ioport.h>
  #include <linux/irq.h>
7f2691a19   Stefan Agner   gpio: vf610: add ...
18
19
20
21
22
23
  #include <linux/platform_device.h>
  #include <linux/of.h>
  #include <linux/of_device.h>
  #include <linux/of_irq.h>
  
  #define VF610_GPIO_PER_PORT		32
659d8a623   Dong Aisheng   gpio: vf610: add ...
24
25
26
27
  struct fsl_gpio_soc_data {
  	/* SoCs has a Port Data Direction Register (PDDR) */
  	bool have_paddr;
  };
7f2691a19   Stefan Agner   gpio: vf610: add ...
28
29
  struct vf610_gpio_port {
  	struct gpio_chip gc;
338aa1075   Andrey Smirnov   gpio: vf610: Do n...
30
  	struct irq_chip ic;
7f2691a19   Stefan Agner   gpio: vf610: add ...
31
32
  	void __iomem *base;
  	void __iomem *gpio_base;
659d8a623   Dong Aisheng   gpio: vf610: add ...
33
  	const struct fsl_gpio_soc_data *sdata;
7f2691a19   Stefan Agner   gpio: vf610: add ...
34
  	u8 irqc[VF610_GPIO_PER_PORT];
91393622b   A.s. Dong   gpio: vf610: add ...
35
36
  	struct clk *clk_port;
  	struct clk *clk_gpio;
7f2691a19   Stefan Agner   gpio: vf610: add ...
37
38
39
40
41
42
43
44
  	int irq;
  };
  
  #define GPIO_PDOR		0x00
  #define GPIO_PSOR		0x04
  #define GPIO_PCOR		0x08
  #define GPIO_PTOR		0x0c
  #define GPIO_PDIR		0x10
659d8a623   Dong Aisheng   gpio: vf610: add ...
45
  #define GPIO_PDDR		0x14
7f2691a19   Stefan Agner   gpio: vf610: add ...
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  
  #define PORT_PCR(n)		((n) * 0x4)
  #define PORT_PCR_IRQC_OFFSET	16
  
  #define PORT_ISFR		0xa0
  #define PORT_DFER		0xc0
  #define PORT_DFCR		0xc4
  #define PORT_DFWR		0xc8
  
  #define PORT_INT_OFF		0x0
  #define PORT_INT_LOGIC_ZERO	0x8
  #define PORT_INT_RISING_EDGE	0x9
  #define PORT_INT_FALLING_EDGE	0xa
  #define PORT_INT_EITHER_EDGE	0xb
  #define PORT_INT_LOGIC_ONE	0xc
659d8a623   Dong Aisheng   gpio: vf610: add ...
61
62
63
  static const struct fsl_gpio_soc_data imx_data = {
  	.have_paddr = true,
  };
7f2691a19   Stefan Agner   gpio: vf610: add ...
64
  static const struct of_device_id vf610_gpio_dt_ids[] = {
659d8a623   Dong Aisheng   gpio: vf610: add ...
65
66
  	{ .compatible = "fsl,vf610-gpio",	.data = NULL, },
  	{ .compatible = "fsl,imx7ulp-gpio",	.data = &imx_data, },
7f2691a19   Stefan Agner   gpio: vf610: add ...
67
68
69
70
71
72
73
74
75
76
77
78
  	{ /* sentinel */ }
  };
  
  static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
  {
  	writel_relaxed(val, reg);
  }
  
  static inline u32 vf610_gpio_readl(void __iomem *reg)
  {
  	return readl_relaxed(reg);
  }
7f2691a19   Stefan Agner   gpio: vf610: add ...
79
80
  static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
  {
65389b490   Linus Walleij   gpio: vf610: use ...
81
  	struct vf610_gpio_port *port = gpiochip_get_data(gc);
659d8a623   Dong Aisheng   gpio: vf610: add ...
82
  	unsigned long mask = BIT(gpio);
4a8909d02   Andrey Smirnov   gpio: vf610: Simp...
83
  	unsigned long offset = GPIO_PDIR;
659d8a623   Dong Aisheng   gpio: vf610: add ...
84
85
86
  
  	if (port->sdata && port->sdata->have_paddr) {
  		mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
4a8909d02   Andrey Smirnov   gpio: vf610: Simp...
87
88
  		if (mask)
  			offset = GPIO_PDOR;
659d8a623   Dong Aisheng   gpio: vf610: add ...
89
  	}
4a8909d02   Andrey Smirnov   gpio: vf610: Simp...
90
91
  
  	return !!(vf610_gpio_readl(port->gpio_base + offset) & BIT(gpio));
7f2691a19   Stefan Agner   gpio: vf610: add ...
92
93
94
95
  }
  
  static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
  {
65389b490   Linus Walleij   gpio: vf610: use ...
96
  	struct vf610_gpio_port *port = gpiochip_get_data(gc);
7f2691a19   Stefan Agner   gpio: vf610: add ...
97
  	unsigned long mask = BIT(gpio);
a262555bc   Andrey Smirnov   gpio: vf610: Simp...
98
  	unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR;
7f2691a19   Stefan Agner   gpio: vf610: add ...
99

a262555bc   Andrey Smirnov   gpio: vf610: Simp...
100
  	vf610_gpio_writel(mask, port->gpio_base + offset);
7f2691a19   Stefan Agner   gpio: vf610: add ...
101
102
103
104
  }
  
  static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
  {
659d8a623   Dong Aisheng   gpio: vf610: add ...
105
106
107
108
109
110
111
112
113
  	struct vf610_gpio_port *port = gpiochip_get_data(chip);
  	unsigned long mask = BIT(gpio);
  	u32 val;
  
  	if (port->sdata && port->sdata->have_paddr) {
  		val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
  		val &= ~mask;
  		vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
  	}
7f2691a19   Stefan Agner   gpio: vf610: add ...
114
115
116
117
118
119
  	return pinctrl_gpio_direction_input(chip->base + gpio);
  }
  
  static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
  				       int value)
  {
659d8a623   Dong Aisheng   gpio: vf610: add ...
120
121
  	struct vf610_gpio_port *port = gpiochip_get_data(chip);
  	unsigned long mask = BIT(gpio);
6b1d525bd   Haibo Chen   LF-6013 gpio: gpi...
122
  	u32 val;
659d8a623   Dong Aisheng   gpio: vf610: add ...
123

6b1d525bd   Haibo Chen   LF-6013 gpio: gpi...
124
125
126
127
128
  	if (port->sdata && port->sdata->have_paddr) {
  		val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
  		val |= mask;
  		vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
  	}
659d8a623   Dong Aisheng   gpio: vf610: add ...
129

7f2691a19   Stefan Agner   gpio: vf610: add ...
130
131
132
133
  	vf610_gpio_set(chip, gpio, value);
  
  	return pinctrl_gpio_direction_output(chip->base + gpio);
  }
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
134
  static void vf610_gpio_irq_handler(struct irq_desc *desc)
7f2691a19   Stefan Agner   gpio: vf610: add ...
135
  {
2f930643c   Linus Walleij   gpio: vf610: use ...
136
  	struct vf610_gpio_port *port =
65389b490   Linus Walleij   gpio: vf610: use ...
137
  		gpiochip_get_data(irq_desc_get_handler_data(desc));
7f2691a19   Stefan Agner   gpio: vf610: add ...
138
139
140
141
142
143
144
145
146
147
  	struct irq_chip *chip = irq_desc_get_chip(desc);
  	int pin;
  	unsigned long irq_isfr;
  
  	chained_irq_enter(chip, desc);
  
  	irq_isfr = vf610_gpio_readl(port->base + PORT_ISFR);
  
  	for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {
  		vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);
dbd1c54fc   Marc Zyngier   gpio: Bulk conver...
148
  		generic_handle_domain_irq(port->gc.irq.domain, pin);
7f2691a19   Stefan Agner   gpio: vf610: add ...
149
150
151
152
153
154
155
  	}
  
  	chained_irq_exit(chip, desc);
  }
  
  static void vf610_gpio_irq_ack(struct irq_data *d)
  {
2f930643c   Linus Walleij   gpio: vf610: use ...
156
  	struct vf610_gpio_port *port =
65389b490   Linus Walleij   gpio: vf610: use ...
157
  		gpiochip_get_data(irq_data_get_irq_chip_data(d));
7f2691a19   Stefan Agner   gpio: vf610: add ...
158
159
160
161
162
163
164
  	int gpio = d->hwirq;
  
  	vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);
  }
  
  static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
  {
2f930643c   Linus Walleij   gpio: vf610: use ...
165
  	struct vf610_gpio_port *port =
65389b490   Linus Walleij   gpio: vf610: use ...
166
  		gpiochip_get_data(irq_data_get_irq_chip_data(d));
7f2691a19   Stefan Agner   gpio: vf610: add ...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  	u8 irqc;
  
  	switch (type) {
  	case IRQ_TYPE_EDGE_RISING:
  		irqc = PORT_INT_RISING_EDGE;
  		break;
  	case IRQ_TYPE_EDGE_FALLING:
  		irqc = PORT_INT_FALLING_EDGE;
  		break;
  	case IRQ_TYPE_EDGE_BOTH:
  		irqc = PORT_INT_EITHER_EDGE;
  		break;
  	case IRQ_TYPE_LEVEL_LOW:
  		irqc = PORT_INT_LOGIC_ZERO;
  		break;
  	case IRQ_TYPE_LEVEL_HIGH:
  		irqc = PORT_INT_LOGIC_ONE;
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	port->irqc[d->hwirq] = irqc;
fd9681157   Stefan Agner   gpio: vf610: hand...
190
  	if (type & IRQ_TYPE_LEVEL_MASK)
a7147db0f   Thomas Gleixner   gpio: vf610: Use ...
191
  		irq_set_handler_locked(d, handle_level_irq);
fd9681157   Stefan Agner   gpio: vf610: hand...
192
  	else
a7147db0f   Thomas Gleixner   gpio: vf610: Use ...
193
  		irq_set_handler_locked(d, handle_edge_irq);
fd9681157   Stefan Agner   gpio: vf610: hand...
194

7f2691a19   Stefan Agner   gpio: vf610: add ...
195
196
197
198
199
  	return 0;
  }
  
  static void vf610_gpio_irq_mask(struct irq_data *d)
  {
2f930643c   Linus Walleij   gpio: vf610: use ...
200
  	struct vf610_gpio_port *port =
65389b490   Linus Walleij   gpio: vf610: use ...
201
  		gpiochip_get_data(irq_data_get_irq_chip_data(d));
7f2691a19   Stefan Agner   gpio: vf610: add ...
202
203
204
205
206
207
208
  	void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
  
  	vf610_gpio_writel(0, pcr_base);
  }
  
  static void vf610_gpio_irq_unmask(struct irq_data *d)
  {
2f930643c   Linus Walleij   gpio: vf610: use ...
209
  	struct vf610_gpio_port *port =
65389b490   Linus Walleij   gpio: vf610: use ...
210
  		gpiochip_get_data(irq_data_get_irq_chip_data(d));
7f2691a19   Stefan Agner   gpio: vf610: add ...
211
212
213
214
215
216
217
218
  	void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
  
  	vf610_gpio_writel(port->irqc[d->hwirq] << PORT_PCR_IRQC_OFFSET,
  			  pcr_base);
  }
  
  static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
  {
2f930643c   Linus Walleij   gpio: vf610: use ...
219
  	struct vf610_gpio_port *port =
65389b490   Linus Walleij   gpio: vf610: use ...
220
  		gpiochip_get_data(irq_data_get_irq_chip_data(d));
7f2691a19   Stefan Agner   gpio: vf610: add ...
221
222
223
224
225
226
227
228
  
  	if (enable)
  		enable_irq_wake(port->irq);
  	else
  		disable_irq_wake(port->irq);
  
  	return 0;
  }
db9ed63ca   Andrey Smirnov   gpio: vf610: Use ...
229
230
231
232
  static void vf610_gpio_disable_clk(void *data)
  {
  	clk_disable_unprepare(data);
  }
7f2691a19   Stefan Agner   gpio: vf610: add ...
233
234
235
236
237
238
  
  static int vf610_gpio_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
  	struct device_node *np = dev->of_node;
  	struct vf610_gpio_port *port;
7f2691a19   Stefan Agner   gpio: vf610: add ...
239
  	struct gpio_chip *gc;
e599256ab   Linus Walleij   gpio: vf610: Pass...
240
  	struct gpio_irq_chip *girq;
338aa1075   Andrey Smirnov   gpio: vf610: Do n...
241
  	struct irq_chip *ic;
7ae710f9f   Andrew Lunn   gpio: vf610: Mask...
242
  	int i;
7f2691a19   Stefan Agner   gpio: vf610: add ...
243
  	int ret;
2e35bb6cd   Andrey Smirnov   gpio: vf610: Don'...
244
  	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
7f2691a19   Stefan Agner   gpio: vf610: add ...
245
246
  	if (!port)
  		return -ENOMEM;
23e577ebe   Thierry Reding   gpio: vf610: Use ...
247
  	port->sdata = of_device_get_match_data(dev);
df53665b4   Enrico Weigelt, metux IT consult   drivers: gpio: vf...
248
  	port->base = devm_platform_ioremap_resource(pdev, 0);
7f2691a19   Stefan Agner   gpio: vf610: add ...
249
250
  	if (IS_ERR(port->base))
  		return PTR_ERR(port->base);
df53665b4   Enrico Weigelt, metux IT consult   drivers: gpio: vf...
251
  	port->gpio_base = devm_platform_ioremap_resource(pdev, 1);
7f2691a19   Stefan Agner   gpio: vf610: add ...
252
253
254
255
256
257
  	if (IS_ERR(port->gpio_base))
  		return PTR_ERR(port->gpio_base);
  
  	port->irq = platform_get_irq(pdev, 0);
  	if (port->irq < 0)
  		return port->irq;
2e35bb6cd   Andrey Smirnov   gpio: vf610: Don'...
258
  	port->clk_port = devm_clk_get(dev, "port");
663ba742d   Andrey Smirnov   gpio: vf610: Use ...
259
260
  	ret = PTR_ERR_OR_ZERO(port->clk_port);
  	if (!ret) {
91393622b   A.s. Dong   gpio: vf610: add ...
261
262
263
  		ret = clk_prepare_enable(port->clk_port);
  		if (ret)
  			return ret;
db9ed63ca   Andrey Smirnov   gpio: vf610: Use ...
264
265
266
267
  		ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,
  					       port->clk_port);
  		if (ret)
  			return ret;
663ba742d   Andrey Smirnov   gpio: vf610: Use ...
268
  	} else if (ret == -EPROBE_DEFER) {
91393622b   A.s. Dong   gpio: vf610: add ...
269
270
271
272
  		/*
  		 * Percolate deferrals, for anything else,
  		 * just live without the clocking.
  		 */
663ba742d   Andrey Smirnov   gpio: vf610: Use ...
273
  		return ret;
91393622b   A.s. Dong   gpio: vf610: add ...
274
  	}
2e35bb6cd   Andrey Smirnov   gpio: vf610: Don'...
275
  	port->clk_gpio = devm_clk_get(dev, "gpio");
663ba742d   Andrey Smirnov   gpio: vf610: Use ...
276
277
  	ret = PTR_ERR_OR_ZERO(port->clk_gpio);
  	if (!ret) {
91393622b   A.s. Dong   gpio: vf610: add ...
278
  		ret = clk_prepare_enable(port->clk_gpio);
db9ed63ca   Andrey Smirnov   gpio: vf610: Use ...
279
  		if (ret)
91393622b   A.s. Dong   gpio: vf610: add ...
280
  			return ret;
fc57949cf   Andrey Smirnov   gpio: vf610: Use ...
281
282
283
  		ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,
  					       port->clk_gpio);
  		if (ret)
91393622b   A.s. Dong   gpio: vf610: add ...
284
  			return ret;
663ba742d   Andrey Smirnov   gpio: vf610: Use ...
285
286
  	} else if (ret == -EPROBE_DEFER) {
  		return ret;
91393622b   A.s. Dong   gpio: vf610: add ...
287
  	}
7f2691a19   Stefan Agner   gpio: vf610: add ...
288
289
  	gc = &port->gc;
  	gc->of_node = np;
58383c784   Linus Walleij   gpio: change memb...
290
  	gc->parent = dev;
bd77e320b   Clark Wang   LF-4273-2 gpio: v...
291
  	gc->label = dev_name(dev);
d32efe379   Axel Lin   gpio: vf610: Repl...
292
  	gc->ngpio = VF610_GPIO_PER_PORT;
7f2691a19   Stefan Agner   gpio: vf610: add ...
293
  	gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
203f0daaf   Jonas Gorski   gpio: replace tri...
294
295
  	gc->request = gpiochip_generic_request;
  	gc->free = gpiochip_generic_free;
d32efe379   Axel Lin   gpio: vf610: Repl...
296
297
298
299
  	gc->direction_input = vf610_gpio_direction_input;
  	gc->get = vf610_gpio_get;
  	gc->direction_output = vf610_gpio_direction_output;
  	gc->set = vf610_gpio_set;
7f2691a19   Stefan Agner   gpio: vf610: add ...
300

338aa1075   Andrey Smirnov   gpio: vf610: Do n...
301
302
303
304
305
306
307
  	ic = &port->ic;
  	ic->name = "gpio-vf610";
  	ic->irq_ack = vf610_gpio_irq_ack;
  	ic->irq_mask = vf610_gpio_irq_mask;
  	ic->irq_unmask = vf610_gpio_irq_unmask;
  	ic->irq_set_type = vf610_gpio_irq_set_type;
  	ic->irq_set_wake = vf610_gpio_irq_set_wake;
7ae710f9f   Andrew Lunn   gpio: vf610: Mask...
308
309
310
  	/* Mask all GPIO interrupts */
  	for (i = 0; i < gc->ngpio; i++)
  		vf610_gpio_writel(0, port->base + PORT_PCR(i));
7f2691a19   Stefan Agner   gpio: vf610: add ...
311
312
  	/* Clear the interrupt status register for all GPIO's */
  	vf610_gpio_writel(~0, port->base + PORT_ISFR);
e599256ab   Linus Walleij   gpio: vf610: Pass...
313
314
315
316
317
318
319
320
321
322
323
324
  	girq = &gc->irq;
  	girq->chip = ic;
  	girq->parent_handler = vf610_gpio_irq_handler;
  	girq->num_parents = 1;
  	girq->parents = devm_kcalloc(&pdev->dev, 1,
  				     sizeof(*girq->parents),
  				     GFP_KERNEL);
  	if (!girq->parents)
  		return -ENOMEM;
  	girq->parents[0] = port->irq;
  	girq->default_type = IRQ_TYPE_NONE;
  	girq->handler = handle_edge_irq;
7f2691a19   Stefan Agner   gpio: vf610: add ...
325

e599256ab   Linus Walleij   gpio: vf610: Pass...
326
  	return devm_gpiochip_add_data(dev, gc, port);
7f2691a19   Stefan Agner   gpio: vf610: add ...
327
328
329
330
331
  }
  
  static struct platform_driver vf610_gpio_driver = {
  	.driver		= {
  		.name	= "gpio-vf610",
7f2691a19   Stefan Agner   gpio: vf610: add ...
332
333
334
335
  		.of_match_table = vf610_gpio_dt_ids,
  	},
  	.probe		= vf610_gpio_probe,
  };
df950da18   Geliang Tang   gpio: vf610: use ...
336
  builtin_platform_driver(vf610_gpio_driver);