xref: /openbmc/linux/drivers/gpio/gpio-mockup.c (revision 8a68ea00a62e7ec33e9e4e7f3d9c2a845943c58d)
10f98dd1bSBamvor Jian Zhang /*
20f98dd1bSBamvor Jian Zhang  * GPIO Testing Device Driver
30f98dd1bSBamvor Jian Zhang  *
40f98dd1bSBamvor Jian Zhang  * Copyright (C) 2014  Kamlakant Patel <kamlakant.patel@broadcom.com>
50f98dd1bSBamvor Jian Zhang  * Copyright (C) 2015-2016  Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
60f98dd1bSBamvor Jian Zhang  *
70f98dd1bSBamvor Jian Zhang  * This program is free software; you can redistribute  it and/or modify it
80f98dd1bSBamvor Jian Zhang  * under  the terms of  the GNU General  Public License as published by the
90f98dd1bSBamvor Jian Zhang  * Free Software Foundation;  either version 2 of the  License, or (at your
100f98dd1bSBamvor Jian Zhang  * option) any later version.
110f98dd1bSBamvor Jian Zhang  *
120f98dd1bSBamvor Jian Zhang  */
130f98dd1bSBamvor Jian Zhang 
140f98dd1bSBamvor Jian Zhang #include <linux/init.h>
150f98dd1bSBamvor Jian Zhang #include <linux/module.h>
160f98dd1bSBamvor Jian Zhang #include <linux/gpio/driver.h>
170f98dd1bSBamvor Jian Zhang #include <linux/platform_device.h>
18*8a68ea00SBartosz Golaszewski #include <linux/slab.h>
190f98dd1bSBamvor Jian Zhang 
20ca409160SBartosz Golaszewski #define GPIO_MOCKUP_NAME	"gpio-mockup"
21ca409160SBartosz Golaszewski #define	GPIO_MOCKUP_MAX_GC	10
220f98dd1bSBamvor Jian Zhang 
23ca409160SBartosz Golaszewski enum {
24ca409160SBartosz Golaszewski 	DIR_IN = 0,
25ca409160SBartosz Golaszewski 	DIR_OUT,
260f98dd1bSBamvor Jian Zhang };
270f98dd1bSBamvor Jian Zhang 
280f98dd1bSBamvor Jian Zhang /*
290f98dd1bSBamvor Jian Zhang  * struct gpio_pin_status - structure describing a GPIO status
300f98dd1bSBamvor Jian Zhang  * @dir:       Configures direction of gpio as "in" or "out", 0=in, 1=out
310f98dd1bSBamvor Jian Zhang  * @value:     Configures status of the gpio as 0(low) or 1(high)
320f98dd1bSBamvor Jian Zhang  */
33ca409160SBartosz Golaszewski struct gpio_mockup_line_status {
34ca409160SBartosz Golaszewski 	int dir;
350f98dd1bSBamvor Jian Zhang 	bool value;
360f98dd1bSBamvor Jian Zhang };
370f98dd1bSBamvor Jian Zhang 
38ca409160SBartosz Golaszewski struct gpio_mockup_chip {
390f98dd1bSBamvor Jian Zhang 	struct gpio_chip gc;
40ca409160SBartosz Golaszewski 	struct gpio_mockup_line_status *lines;
410f98dd1bSBamvor Jian Zhang };
420f98dd1bSBamvor Jian Zhang 
43ca409160SBartosz Golaszewski static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1];
440f98dd1bSBamvor Jian Zhang static int gpio_mockup_params_nr;
450f98dd1bSBamvor Jian Zhang module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400);
460f98dd1bSBamvor Jian Zhang 
47*8a68ea00SBartosz Golaszewski static bool gpio_mockup_named_lines;
48*8a68ea00SBartosz Golaszewski module_param_named(gpio_mockup_named_lines,
49*8a68ea00SBartosz Golaszewski 		   gpio_mockup_named_lines, bool, 0400);
50*8a68ea00SBartosz Golaszewski 
51ca409160SBartosz Golaszewski static const char gpio_mockup_name_start = 'A';
520f98dd1bSBamvor Jian Zhang 
53ca409160SBartosz Golaszewski static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)
540f98dd1bSBamvor Jian Zhang {
55ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
560f98dd1bSBamvor Jian Zhang 
57ca409160SBartosz Golaszewski 	return chip->lines[offset].value;
580f98dd1bSBamvor Jian Zhang }
590f98dd1bSBamvor Jian Zhang 
60ca409160SBartosz Golaszewski static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset,
610f98dd1bSBamvor Jian Zhang 			    int value)
620f98dd1bSBamvor Jian Zhang {
63ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
640f98dd1bSBamvor Jian Zhang 
65ca409160SBartosz Golaszewski 	chip->lines[offset].value = !!value;
660f98dd1bSBamvor Jian Zhang }
670f98dd1bSBamvor Jian Zhang 
68ca409160SBartosz Golaszewski static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset,
690f98dd1bSBamvor Jian Zhang 			      int value)
700f98dd1bSBamvor Jian Zhang {
71ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
720f98dd1bSBamvor Jian Zhang 
73ca409160SBartosz Golaszewski 	gpio_mockup_set(gc, offset, value);
74ca409160SBartosz Golaszewski 	chip->lines[offset].dir = DIR_OUT;
75ca409160SBartosz Golaszewski 
760f98dd1bSBamvor Jian Zhang 	return 0;
770f98dd1bSBamvor Jian Zhang }
780f98dd1bSBamvor Jian Zhang 
79ca409160SBartosz Golaszewski static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
800f98dd1bSBamvor Jian Zhang {
81ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
820f98dd1bSBamvor Jian Zhang 
83ca409160SBartosz Golaszewski 	chip->lines[offset].dir = DIR_IN;
84ca409160SBartosz Golaszewski 
850f98dd1bSBamvor Jian Zhang 	return 0;
860f98dd1bSBamvor Jian Zhang }
870f98dd1bSBamvor Jian Zhang 
88ca409160SBartosz Golaszewski static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
890f98dd1bSBamvor Jian Zhang {
90ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
910f98dd1bSBamvor Jian Zhang 
92ca409160SBartosz Golaszewski 	return chip->lines[offset].dir;
930f98dd1bSBamvor Jian Zhang }
940f98dd1bSBamvor Jian Zhang 
95*8a68ea00SBartosz Golaszewski static int gpio_mockup_name_lines(struct device *dev,
96*8a68ea00SBartosz Golaszewski 				  struct gpio_mockup_chip *chip)
97*8a68ea00SBartosz Golaszewski {
98*8a68ea00SBartosz Golaszewski 	struct gpio_chip *gc = &chip->gc;
99*8a68ea00SBartosz Golaszewski 	char **names;
100*8a68ea00SBartosz Golaszewski 	int i;
101*8a68ea00SBartosz Golaszewski 
102*8a68ea00SBartosz Golaszewski 	names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL);
103*8a68ea00SBartosz Golaszewski 	if (!names)
104*8a68ea00SBartosz Golaszewski 		return -ENOMEM;
105*8a68ea00SBartosz Golaszewski 
106*8a68ea00SBartosz Golaszewski 	for (i = 0; i < gc->ngpio; i++) {
107*8a68ea00SBartosz Golaszewski 		names[i] = devm_kasprintf(dev, GFP_KERNEL,
108*8a68ea00SBartosz Golaszewski 					  "%s-%d", gc->label, i);
109*8a68ea00SBartosz Golaszewski 		if (!names[i])
110*8a68ea00SBartosz Golaszewski 			return -ENOMEM;
111*8a68ea00SBartosz Golaszewski 	}
112*8a68ea00SBartosz Golaszewski 
113*8a68ea00SBartosz Golaszewski 	gc->names = (const char *const *)names;
114*8a68ea00SBartosz Golaszewski 
115*8a68ea00SBartosz Golaszewski 	return 0;
116*8a68ea00SBartosz Golaszewski }
117*8a68ea00SBartosz Golaszewski 
118ca409160SBartosz Golaszewski static int gpio_mockup_add(struct device *dev,
119ca409160SBartosz Golaszewski 			   struct gpio_mockup_chip *chip,
1200f98dd1bSBamvor Jian Zhang 			   const char *name, int base, int ngpio)
1210f98dd1bSBamvor Jian Zhang {
122ca409160SBartosz Golaszewski 	struct gpio_chip *gc = &chip->gc;
123*8a68ea00SBartosz Golaszewski 	int ret;
1240f98dd1bSBamvor Jian Zhang 
125ca409160SBartosz Golaszewski 	gc->base = base;
126ca409160SBartosz Golaszewski 	gc->ngpio = ngpio;
127ca409160SBartosz Golaszewski 	gc->label = name;
128ca409160SBartosz Golaszewski 	gc->owner = THIS_MODULE;
129ca409160SBartosz Golaszewski 	gc->parent = dev;
130ca409160SBartosz Golaszewski 	gc->get = gpio_mockup_get;
131ca409160SBartosz Golaszewski 	gc->set = gpio_mockup_set;
132ca409160SBartosz Golaszewski 	gc->direction_output = gpio_mockup_dirout;
133ca409160SBartosz Golaszewski 	gc->direction_input = gpio_mockup_dirin;
134ca409160SBartosz Golaszewski 	gc->get_direction = gpio_mockup_get_direction;
135ca409160SBartosz Golaszewski 
136ca409160SBartosz Golaszewski 	chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
1370f98dd1bSBamvor Jian Zhang 				   GFP_KERNEL);
138e4ba07bfSBartosz Golaszewski 	if (!chip->lines)
139e4ba07bfSBartosz Golaszewski 		return -ENOMEM;
140ca409160SBartosz Golaszewski 
141*8a68ea00SBartosz Golaszewski 	if (gpio_mockup_named_lines) {
142*8a68ea00SBartosz Golaszewski 		ret = gpio_mockup_name_lines(dev, chip);
143*8a68ea00SBartosz Golaszewski 		if (ret)
144*8a68ea00SBartosz Golaszewski 			return ret;
145*8a68ea00SBartosz Golaszewski 	}
146*8a68ea00SBartosz Golaszewski 
147e4ba07bfSBartosz Golaszewski 	return devm_gpiochip_add_data(dev, &chip->gc, chip);
1480f98dd1bSBamvor Jian Zhang }
1490f98dd1bSBamvor Jian Zhang 
150ca409160SBartosz Golaszewski static int gpio_mockup_probe(struct platform_device *pdev)
1510f98dd1bSBamvor Jian Zhang {
152ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chips;
153364ac456SBartosz Golaszewski 	struct device *dev = &pdev->dev;
154364ac456SBartosz Golaszewski 	int ret, i, base, ngpio;
155ad6d8004SBartosz Golaszewski 	char *chip_name;
1560f98dd1bSBamvor Jian Zhang 
1570f98dd1bSBamvor Jian Zhang 	if (gpio_mockup_params_nr < 2)
1580f98dd1bSBamvor Jian Zhang 		return -EINVAL;
1590f98dd1bSBamvor Jian Zhang 
160ca409160SBartosz Golaszewski 	chips = devm_kzalloc(dev,
161ca409160SBartosz Golaszewski 			     sizeof(*chips) * (gpio_mockup_params_nr >> 1),
1620f98dd1bSBamvor Jian Zhang 			     GFP_KERNEL);
163ca409160SBartosz Golaszewski 	if (!chips)
1640f98dd1bSBamvor Jian Zhang 		return -ENOMEM;
1650f98dd1bSBamvor Jian Zhang 
166ca409160SBartosz Golaszewski 	platform_set_drvdata(pdev, chips);
1670f98dd1bSBamvor Jian Zhang 
1680f98dd1bSBamvor Jian Zhang 	for (i = 0; i < gpio_mockup_params_nr >> 1; i++) {
1690f98dd1bSBamvor Jian Zhang 		base = gpio_mockup_ranges[i * 2];
170ca409160SBartosz Golaszewski 
1710f98dd1bSBamvor Jian Zhang 		if (base == -1)
1720f98dd1bSBamvor Jian Zhang 			ngpio = gpio_mockup_ranges[i * 2 + 1];
1730f98dd1bSBamvor Jian Zhang 		else
1740f98dd1bSBamvor Jian Zhang 			ngpio = gpio_mockup_ranges[i * 2 + 1] - base;
1750f98dd1bSBamvor Jian Zhang 
1760f98dd1bSBamvor Jian Zhang 		if (ngpio >= 0) {
177ad6d8004SBartosz Golaszewski 			chip_name = devm_kasprintf(dev, GFP_KERNEL,
178ca409160SBartosz Golaszewski 						   "%s-%c", GPIO_MOCKUP_NAME,
179ca409160SBartosz Golaszewski 						   gpio_mockup_name_start + i);
180ad6d8004SBartosz Golaszewski 			if (!chip_name)
181ad6d8004SBartosz Golaszewski 				return -ENOMEM;
182ad6d8004SBartosz Golaszewski 
183ca409160SBartosz Golaszewski 			ret = gpio_mockup_add(dev, &chips[i],
1840f98dd1bSBamvor Jian Zhang 					      chip_name, base, ngpio);
1850f98dd1bSBamvor Jian Zhang 		} else {
1860f98dd1bSBamvor Jian Zhang 			ret = -1;
1870f98dd1bSBamvor Jian Zhang 		}
188ca409160SBartosz Golaszewski 
1890f98dd1bSBamvor Jian Zhang 		if (ret) {
1900f98dd1bSBamvor Jian Zhang 			dev_err(dev, "gpio<%d..%d> add failed\n",
191e4ba07bfSBartosz Golaszewski 				base, base < 0 ? ngpio : base + ngpio);
1920f98dd1bSBamvor Jian Zhang 
1930f98dd1bSBamvor Jian Zhang 			return ret;
1940f98dd1bSBamvor Jian Zhang 		}
195e4ba07bfSBartosz Golaszewski 
196e4ba07bfSBartosz Golaszewski 		dev_info(dev, "gpio<%d..%d> add successful!",
197e4ba07bfSBartosz Golaszewski 			 base, base + ngpio);
1980f98dd1bSBamvor Jian Zhang 	}
1990f98dd1bSBamvor Jian Zhang 
2000f98dd1bSBamvor Jian Zhang 	return 0;
2010f98dd1bSBamvor Jian Zhang }
2020f98dd1bSBamvor Jian Zhang 
203ca409160SBartosz Golaszewski static struct platform_driver gpio_mockup_driver = {
2040f98dd1bSBamvor Jian Zhang 	.driver = {
205ca409160SBartosz Golaszewski 		.name = GPIO_MOCKUP_NAME,
2060f98dd1bSBamvor Jian Zhang 	},
207ca409160SBartosz Golaszewski 	.probe = gpio_mockup_probe,
2080f98dd1bSBamvor Jian Zhang };
2090f98dd1bSBamvor Jian Zhang 
2100f98dd1bSBamvor Jian Zhang static struct platform_device *pdev;
2110f98dd1bSBamvor Jian Zhang static int __init mock_device_init(void)
2120f98dd1bSBamvor Jian Zhang {
2130f98dd1bSBamvor Jian Zhang 	int err;
2140f98dd1bSBamvor Jian Zhang 
215ca409160SBartosz Golaszewski 	pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1);
2160f98dd1bSBamvor Jian Zhang 	if (!pdev)
2170f98dd1bSBamvor Jian Zhang 		return -ENOMEM;
2180f98dd1bSBamvor Jian Zhang 
2190f98dd1bSBamvor Jian Zhang 	err = platform_device_add(pdev);
2200f98dd1bSBamvor Jian Zhang 	if (err) {
2210f98dd1bSBamvor Jian Zhang 		platform_device_put(pdev);
2220f98dd1bSBamvor Jian Zhang 		return err;
2230f98dd1bSBamvor Jian Zhang 	}
2240f98dd1bSBamvor Jian Zhang 
225ca409160SBartosz Golaszewski 	err = platform_driver_register(&gpio_mockup_driver);
2260f98dd1bSBamvor Jian Zhang 	if (err) {
2270f98dd1bSBamvor Jian Zhang 		platform_device_unregister(pdev);
2280f98dd1bSBamvor Jian Zhang 		return err;
2290f98dd1bSBamvor Jian Zhang 	}
2300f98dd1bSBamvor Jian Zhang 
2310f98dd1bSBamvor Jian Zhang 	return 0;
2320f98dd1bSBamvor Jian Zhang }
2330f98dd1bSBamvor Jian Zhang 
2340f98dd1bSBamvor Jian Zhang static void __exit mock_device_exit(void)
2350f98dd1bSBamvor Jian Zhang {
236ca409160SBartosz Golaszewski 	platform_driver_unregister(&gpio_mockup_driver);
2370f98dd1bSBamvor Jian Zhang 	platform_device_unregister(pdev);
2380f98dd1bSBamvor Jian Zhang }
2390f98dd1bSBamvor Jian Zhang 
2400f98dd1bSBamvor Jian Zhang module_init(mock_device_init);
2410f98dd1bSBamvor Jian Zhang module_exit(mock_device_exit);
2420f98dd1bSBamvor Jian Zhang 
2430f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
2440f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
2450f98dd1bSBamvor Jian Zhang MODULE_DESCRIPTION("GPIO Testing driver");
2460f98dd1bSBamvor Jian Zhang MODULE_LICENSE("GPL v2");
247