xref: /linux/drivers/gpio/gpio-mockup.c (revision e2ff7408953f6fe8a341b31ed2d848f73606bbf4)
1 /*
2  * GPIO Testing Device Driver
3  *
4  * Copyright (C) 2014  Kamlakant Patel <kamlakant.patel@broadcom.com>
5  * Copyright (C) 2015-2016  Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
6  *
7  * This program is free software; you can redistribute  it and/or modify it
8  * under  the terms of  the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  *
12  */
13 
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/gpio/driver.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
19 #include <linux/interrupt.h>
20 #include <linux/irq.h>
21 #include <linux/irq_work.h>
22 
23 #define GPIO_MOCKUP_NAME	"gpio-mockup"
24 #define	GPIO_MOCKUP_MAX_GC	10
25 
26 enum {
27 	DIR_IN = 0,
28 	DIR_OUT,
29 };
30 
31 /*
32  * struct gpio_pin_status - structure describing a GPIO status
33  * @dir:       Configures direction of gpio as "in" or "out", 0=in, 1=out
34  * @value:     Configures status of the gpio as 0(low) or 1(high)
35  */
36 struct gpio_mockup_line_status {
37 	int dir;
38 	bool value;
39 };
40 
41 struct gpio_mockup_irq_context {
42 	struct irq_work work;
43 	int irq;
44 };
45 
46 struct gpio_mockup_chip {
47 	struct gpio_chip gc;
48 	struct gpio_mockup_line_status *lines;
49 	struct gpio_mockup_irq_context irq_ctx;
50 };
51 
52 static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1];
53 static int gpio_mockup_params_nr;
54 module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400);
55 
56 static bool gpio_mockup_named_lines;
57 module_param_named(gpio_mockup_named_lines,
58 		   gpio_mockup_named_lines, bool, 0400);
59 
60 static const char gpio_mockup_name_start = 'A';
61 
62 static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)
63 {
64 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
65 
66 	return chip->lines[offset].value;
67 }
68 
69 static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset,
70 			    int value)
71 {
72 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
73 
74 	chip->lines[offset].value = !!value;
75 }
76 
77 static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset,
78 			      int value)
79 {
80 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
81 
82 	gpio_mockup_set(gc, offset, value);
83 	chip->lines[offset].dir = DIR_OUT;
84 
85 	return 0;
86 }
87 
88 static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
89 {
90 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
91 
92 	chip->lines[offset].dir = DIR_IN;
93 
94 	return 0;
95 }
96 
97 static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
98 {
99 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
100 
101 	return chip->lines[offset].dir;
102 }
103 
104 static int gpio_mockup_name_lines(struct device *dev,
105 				  struct gpio_mockup_chip *chip)
106 {
107 	struct gpio_chip *gc = &chip->gc;
108 	char **names;
109 	int i;
110 
111 	names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL);
112 	if (!names)
113 		return -ENOMEM;
114 
115 	for (i = 0; i < gc->ngpio; i++) {
116 		names[i] = devm_kasprintf(dev, GFP_KERNEL,
117 					  "%s-%d", gc->label, i);
118 		if (!names[i])
119 			return -ENOMEM;
120 	}
121 
122 	gc->names = (const char *const *)names;
123 
124 	return 0;
125 }
126 
127 static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset)
128 {
129 	return chip->irq_base + offset;
130 }
131 
132 /*
133  * While we should generally support irqmask and irqunmask, this driver is
134  * for testing purposes only so we don't care.
135  */
136 static void gpio_mockup_irqmask(struct irq_data *d) { }
137 static void gpio_mockup_irqunmask(struct irq_data *d) { }
138 
139 static struct irq_chip gpio_mockup_irqchip = {
140 	.name		= GPIO_MOCKUP_NAME,
141 	.irq_mask	= gpio_mockup_irqmask,
142 	.irq_unmask	= gpio_mockup_irqunmask,
143 };
144 
145 static void gpio_mockup_handle_irq(struct irq_work *work)
146 {
147 	struct gpio_mockup_irq_context *irq_ctx;
148 
149 	irq_ctx = container_of(work, struct gpio_mockup_irq_context, work);
150 	handle_simple_irq(irq_to_desc(irq_ctx->irq));
151 }
152 
153 static int gpio_mockup_irqchip_setup(struct device *dev,
154 				     struct gpio_mockup_chip *chip)
155 {
156 	struct gpio_chip *gc = &chip->gc;
157 	int irq_base, i;
158 
159 	irq_base = irq_alloc_descs(-1, 0, gc->ngpio, 0);
160 	if (irq_base < 0)
161 		return irq_base;
162 
163 	gc->irq_base = irq_base;
164 	gc->irqchip = &gpio_mockup_irqchip;
165 
166 	for (i = 0; i < gc->ngpio; i++) {
167 		irq_set_chip(irq_base + i, gc->irqchip);
168 		irq_set_handler(irq_base + i, &handle_simple_irq);
169 		irq_modify_status(irq_base + i,
170 				  IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
171 	}
172 
173 	init_irq_work(&chip->irq_ctx.work, gpio_mockup_handle_irq);
174 
175 	return 0;
176 }
177 
178 static int gpio_mockup_add(struct device *dev,
179 			   struct gpio_mockup_chip *chip,
180 			   const char *name, int base, int ngpio)
181 {
182 	struct gpio_chip *gc = &chip->gc;
183 	int ret;
184 
185 	gc->base = base;
186 	gc->ngpio = ngpio;
187 	gc->label = name;
188 	gc->owner = THIS_MODULE;
189 	gc->parent = dev;
190 	gc->get = gpio_mockup_get;
191 	gc->set = gpio_mockup_set;
192 	gc->direction_output = gpio_mockup_dirout;
193 	gc->direction_input = gpio_mockup_dirin;
194 	gc->get_direction = gpio_mockup_get_direction;
195 	gc->to_irq = gpio_mockup_to_irq;
196 
197 	chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
198 				   GFP_KERNEL);
199 	if (!chip->lines)
200 		return -ENOMEM;
201 
202 	if (gpio_mockup_named_lines) {
203 		ret = gpio_mockup_name_lines(dev, chip);
204 		if (ret)
205 			return ret;
206 	}
207 
208 	ret = gpio_mockup_irqchip_setup(dev, chip);
209 	if (ret)
210 		return ret;
211 
212 	return devm_gpiochip_add_data(dev, &chip->gc, chip);
213 }
214 
215 static int gpio_mockup_probe(struct platform_device *pdev)
216 {
217 	struct gpio_mockup_chip *chips;
218 	struct device *dev = &pdev->dev;
219 	int ret, i, base, ngpio;
220 	char *chip_name;
221 
222 	if (gpio_mockup_params_nr < 2)
223 		return -EINVAL;
224 
225 	chips = devm_kzalloc(dev,
226 			     sizeof(*chips) * (gpio_mockup_params_nr >> 1),
227 			     GFP_KERNEL);
228 	if (!chips)
229 		return -ENOMEM;
230 
231 	platform_set_drvdata(pdev, chips);
232 
233 	for (i = 0; i < gpio_mockup_params_nr >> 1; i++) {
234 		base = gpio_mockup_ranges[i * 2];
235 
236 		if (base == -1)
237 			ngpio = gpio_mockup_ranges[i * 2 + 1];
238 		else
239 			ngpio = gpio_mockup_ranges[i * 2 + 1] - base;
240 
241 		if (ngpio >= 0) {
242 			chip_name = devm_kasprintf(dev, GFP_KERNEL,
243 						   "%s-%c", GPIO_MOCKUP_NAME,
244 						   gpio_mockup_name_start + i);
245 			if (!chip_name)
246 				return -ENOMEM;
247 
248 			ret = gpio_mockup_add(dev, &chips[i],
249 					      chip_name, base, ngpio);
250 		} else {
251 			ret = -1;
252 		}
253 
254 		if (ret) {
255 			dev_err(dev, "gpio<%d..%d> add failed\n",
256 				base, base < 0 ? ngpio : base + ngpio);
257 
258 			return ret;
259 		}
260 
261 		dev_info(dev, "gpio<%d..%d> add successful!",
262 			 base, base + ngpio);
263 	}
264 
265 	return 0;
266 }
267 
268 static int gpio_mockup_remove(struct platform_device *pdev)
269 {
270 	struct gpio_mockup_chip *chips;
271 	int i;
272 
273 	chips = platform_get_drvdata(pdev);
274 
275 	for (i = 0; i < gpio_mockup_params_nr >> 1; i++)
276 		irq_free_descs(chips[i].gc.irq_base, chips[i].gc.ngpio);
277 
278 	return 0;
279 }
280 
281 static struct platform_driver gpio_mockup_driver = {
282 	.driver = {
283 		.name = GPIO_MOCKUP_NAME,
284 	},
285 	.probe = gpio_mockup_probe,
286 	.remove = gpio_mockup_remove,
287 };
288 
289 static struct platform_device *pdev;
290 static int __init mock_device_init(void)
291 {
292 	int err;
293 
294 	pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1);
295 	if (!pdev)
296 		return -ENOMEM;
297 
298 	err = platform_device_add(pdev);
299 	if (err) {
300 		platform_device_put(pdev);
301 		return err;
302 	}
303 
304 	err = platform_driver_register(&gpio_mockup_driver);
305 	if (err) {
306 		platform_device_unregister(pdev);
307 		return err;
308 	}
309 
310 	return 0;
311 }
312 
313 static void __exit mock_device_exit(void)
314 {
315 	platform_driver_unregister(&gpio_mockup_driver);
316 	platform_device_unregister(pdev);
317 }
318 
319 module_init(mock_device_init);
320 module_exit(mock_device_exit);
321 
322 MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
323 MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
324 MODULE_DESCRIPTION("GPIO Testing driver");
325 MODULE_LICENSE("GPL v2");
326