xref: /linux/drivers/gpio/gpio-mockup.c (revision 8a68ea00a62e7ec33e9e4e7f3d9c2a845943c58d)
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 
20 #define GPIO_MOCKUP_NAME	"gpio-mockup"
21 #define	GPIO_MOCKUP_MAX_GC	10
22 
23 enum {
24 	DIR_IN = 0,
25 	DIR_OUT,
26 };
27 
28 /*
29  * struct gpio_pin_status - structure describing a GPIO status
30  * @dir:       Configures direction of gpio as "in" or "out", 0=in, 1=out
31  * @value:     Configures status of the gpio as 0(low) or 1(high)
32  */
33 struct gpio_mockup_line_status {
34 	int dir;
35 	bool value;
36 };
37 
38 struct gpio_mockup_chip {
39 	struct gpio_chip gc;
40 	struct gpio_mockup_line_status *lines;
41 };
42 
43 static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1];
44 static int gpio_mockup_params_nr;
45 module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400);
46 
47 static bool gpio_mockup_named_lines;
48 module_param_named(gpio_mockup_named_lines,
49 		   gpio_mockup_named_lines, bool, 0400);
50 
51 static const char gpio_mockup_name_start = 'A';
52 
53 static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)
54 {
55 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
56 
57 	return chip->lines[offset].value;
58 }
59 
60 static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset,
61 			    int value)
62 {
63 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
64 
65 	chip->lines[offset].value = !!value;
66 }
67 
68 static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset,
69 			      int value)
70 {
71 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
72 
73 	gpio_mockup_set(gc, offset, value);
74 	chip->lines[offset].dir = DIR_OUT;
75 
76 	return 0;
77 }
78 
79 static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
80 {
81 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
82 
83 	chip->lines[offset].dir = DIR_IN;
84 
85 	return 0;
86 }
87 
88 static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
89 {
90 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
91 
92 	return chip->lines[offset].dir;
93 }
94 
95 static int gpio_mockup_name_lines(struct device *dev,
96 				  struct gpio_mockup_chip *chip)
97 {
98 	struct gpio_chip *gc = &chip->gc;
99 	char **names;
100 	int i;
101 
102 	names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL);
103 	if (!names)
104 		return -ENOMEM;
105 
106 	for (i = 0; i < gc->ngpio; i++) {
107 		names[i] = devm_kasprintf(dev, GFP_KERNEL,
108 					  "%s-%d", gc->label, i);
109 		if (!names[i])
110 			return -ENOMEM;
111 	}
112 
113 	gc->names = (const char *const *)names;
114 
115 	return 0;
116 }
117 
118 static int gpio_mockup_add(struct device *dev,
119 			   struct gpio_mockup_chip *chip,
120 			   const char *name, int base, int ngpio)
121 {
122 	struct gpio_chip *gc = &chip->gc;
123 	int ret;
124 
125 	gc->base = base;
126 	gc->ngpio = ngpio;
127 	gc->label = name;
128 	gc->owner = THIS_MODULE;
129 	gc->parent = dev;
130 	gc->get = gpio_mockup_get;
131 	gc->set = gpio_mockup_set;
132 	gc->direction_output = gpio_mockup_dirout;
133 	gc->direction_input = gpio_mockup_dirin;
134 	gc->get_direction = gpio_mockup_get_direction;
135 
136 	chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
137 				   GFP_KERNEL);
138 	if (!chip->lines)
139 		return -ENOMEM;
140 
141 	if (gpio_mockup_named_lines) {
142 		ret = gpio_mockup_name_lines(dev, chip);
143 		if (ret)
144 			return ret;
145 	}
146 
147 	return devm_gpiochip_add_data(dev, &chip->gc, chip);
148 }
149 
150 static int gpio_mockup_probe(struct platform_device *pdev)
151 {
152 	struct gpio_mockup_chip *chips;
153 	struct device *dev = &pdev->dev;
154 	int ret, i, base, ngpio;
155 	char *chip_name;
156 
157 	if (gpio_mockup_params_nr < 2)
158 		return -EINVAL;
159 
160 	chips = devm_kzalloc(dev,
161 			     sizeof(*chips) * (gpio_mockup_params_nr >> 1),
162 			     GFP_KERNEL);
163 	if (!chips)
164 		return -ENOMEM;
165 
166 	platform_set_drvdata(pdev, chips);
167 
168 	for (i = 0; i < gpio_mockup_params_nr >> 1; i++) {
169 		base = gpio_mockup_ranges[i * 2];
170 
171 		if (base == -1)
172 			ngpio = gpio_mockup_ranges[i * 2 + 1];
173 		else
174 			ngpio = gpio_mockup_ranges[i * 2 + 1] - base;
175 
176 		if (ngpio >= 0) {
177 			chip_name = devm_kasprintf(dev, GFP_KERNEL,
178 						   "%s-%c", GPIO_MOCKUP_NAME,
179 						   gpio_mockup_name_start + i);
180 			if (!chip_name)
181 				return -ENOMEM;
182 
183 			ret = gpio_mockup_add(dev, &chips[i],
184 					      chip_name, base, ngpio);
185 		} else {
186 			ret = -1;
187 		}
188 
189 		if (ret) {
190 			dev_err(dev, "gpio<%d..%d> add failed\n",
191 				base, base < 0 ? ngpio : base + ngpio);
192 
193 			return ret;
194 		}
195 
196 		dev_info(dev, "gpio<%d..%d> add successful!",
197 			 base, base + ngpio);
198 	}
199 
200 	return 0;
201 }
202 
203 static struct platform_driver gpio_mockup_driver = {
204 	.driver = {
205 		.name = GPIO_MOCKUP_NAME,
206 	},
207 	.probe = gpio_mockup_probe,
208 };
209 
210 static struct platform_device *pdev;
211 static int __init mock_device_init(void)
212 {
213 	int err;
214 
215 	pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1);
216 	if (!pdev)
217 		return -ENOMEM;
218 
219 	err = platform_device_add(pdev);
220 	if (err) {
221 		platform_device_put(pdev);
222 		return err;
223 	}
224 
225 	err = platform_driver_register(&gpio_mockup_driver);
226 	if (err) {
227 		platform_device_unregister(pdev);
228 		return err;
229 	}
230 
231 	return 0;
232 }
233 
234 static void __exit mock_device_exit(void)
235 {
236 	platform_driver_unregister(&gpio_mockup_driver);
237 	platform_device_unregister(pdev);
238 }
239 
240 module_init(mock_device_init);
241 module_exit(mock_device_exit);
242 
243 MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
244 MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
245 MODULE_DESCRIPTION("GPIO Testing driver");
246 MODULE_LICENSE("GPL v2");
247