xref: /openbmc/linux/drivers/gpio/gpio-mockup.c (revision ca4091607847d25778db1b701a6e14dcc87a55ff)
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>
180f98dd1bSBamvor Jian Zhang 
19*ca409160SBartosz Golaszewski #define GPIO_MOCKUP_NAME	"gpio-mockup"
20*ca409160SBartosz Golaszewski #define	GPIO_MOCKUP_MAX_GC	10
210f98dd1bSBamvor Jian Zhang 
22*ca409160SBartosz Golaszewski enum {
23*ca409160SBartosz Golaszewski 	DIR_IN = 0,
24*ca409160SBartosz Golaszewski 	DIR_OUT,
250f98dd1bSBamvor Jian Zhang };
260f98dd1bSBamvor Jian Zhang 
270f98dd1bSBamvor Jian Zhang /*
280f98dd1bSBamvor Jian Zhang  * struct gpio_pin_status - structure describing a GPIO status
290f98dd1bSBamvor Jian Zhang  * @dir:       Configures direction of gpio as "in" or "out", 0=in, 1=out
300f98dd1bSBamvor Jian Zhang  * @value:     Configures status of the gpio as 0(low) or 1(high)
310f98dd1bSBamvor Jian Zhang  */
32*ca409160SBartosz Golaszewski struct gpio_mockup_line_status {
33*ca409160SBartosz Golaszewski 	int dir;
340f98dd1bSBamvor Jian Zhang 	bool value;
350f98dd1bSBamvor Jian Zhang };
360f98dd1bSBamvor Jian Zhang 
37*ca409160SBartosz Golaszewski struct gpio_mockup_chip {
380f98dd1bSBamvor Jian Zhang 	struct gpio_chip gc;
39*ca409160SBartosz Golaszewski 	struct gpio_mockup_line_status *lines;
400f98dd1bSBamvor Jian Zhang };
410f98dd1bSBamvor Jian Zhang 
42*ca409160SBartosz Golaszewski static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1];
430f98dd1bSBamvor Jian Zhang static int gpio_mockup_params_nr;
440f98dd1bSBamvor Jian Zhang module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400);
450f98dd1bSBamvor Jian Zhang 
46*ca409160SBartosz Golaszewski static const char gpio_mockup_name_start = 'A';
470f98dd1bSBamvor Jian Zhang 
48*ca409160SBartosz Golaszewski static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)
490f98dd1bSBamvor Jian Zhang {
50*ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
510f98dd1bSBamvor Jian Zhang 
52*ca409160SBartosz Golaszewski 	return chip->lines[offset].value;
530f98dd1bSBamvor Jian Zhang }
540f98dd1bSBamvor Jian Zhang 
55*ca409160SBartosz Golaszewski static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset,
560f98dd1bSBamvor Jian Zhang 			    int value)
570f98dd1bSBamvor Jian Zhang {
58*ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
590f98dd1bSBamvor Jian Zhang 
60*ca409160SBartosz Golaszewski 	chip->lines[offset].value = !!value;
610f98dd1bSBamvor Jian Zhang }
620f98dd1bSBamvor Jian Zhang 
63*ca409160SBartosz Golaszewski static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset,
640f98dd1bSBamvor Jian Zhang 			      int value)
650f98dd1bSBamvor Jian Zhang {
66*ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
670f98dd1bSBamvor Jian Zhang 
68*ca409160SBartosz Golaszewski 	gpio_mockup_set(gc, offset, value);
69*ca409160SBartosz Golaszewski 	chip->lines[offset].dir = DIR_OUT;
70*ca409160SBartosz Golaszewski 
710f98dd1bSBamvor Jian Zhang 	return 0;
720f98dd1bSBamvor Jian Zhang }
730f98dd1bSBamvor Jian Zhang 
74*ca409160SBartosz Golaszewski static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
750f98dd1bSBamvor Jian Zhang {
76*ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
770f98dd1bSBamvor Jian Zhang 
78*ca409160SBartosz Golaszewski 	chip->lines[offset].dir = DIR_IN;
79*ca409160SBartosz Golaszewski 
800f98dd1bSBamvor Jian Zhang 	return 0;
810f98dd1bSBamvor Jian Zhang }
820f98dd1bSBamvor Jian Zhang 
83*ca409160SBartosz Golaszewski static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
840f98dd1bSBamvor Jian Zhang {
85*ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
860f98dd1bSBamvor Jian Zhang 
87*ca409160SBartosz Golaszewski 	return chip->lines[offset].dir;
880f98dd1bSBamvor Jian Zhang }
890f98dd1bSBamvor Jian Zhang 
90*ca409160SBartosz Golaszewski static int gpio_mockup_add(struct device *dev,
91*ca409160SBartosz Golaszewski 			   struct gpio_mockup_chip *chip,
920f98dd1bSBamvor Jian Zhang 			   const char *name, int base, int ngpio)
930f98dd1bSBamvor Jian Zhang {
94*ca409160SBartosz Golaszewski 	struct gpio_chip *gc = &chip->gc;
950f98dd1bSBamvor Jian Zhang 	int ret;
960f98dd1bSBamvor Jian Zhang 
97*ca409160SBartosz Golaszewski 	gc->base = base;
98*ca409160SBartosz Golaszewski 	gc->ngpio = ngpio;
99*ca409160SBartosz Golaszewski 	gc->label = name;
100*ca409160SBartosz Golaszewski 	gc->owner = THIS_MODULE;
101*ca409160SBartosz Golaszewski 	gc->parent = dev;
102*ca409160SBartosz Golaszewski 	gc->get = gpio_mockup_get;
103*ca409160SBartosz Golaszewski 	gc->set = gpio_mockup_set;
104*ca409160SBartosz Golaszewski 	gc->direction_output = gpio_mockup_dirout;
105*ca409160SBartosz Golaszewski 	gc->direction_input = gpio_mockup_dirin;
106*ca409160SBartosz Golaszewski 	gc->get_direction = gpio_mockup_get_direction;
107*ca409160SBartosz Golaszewski 
108*ca409160SBartosz Golaszewski 	chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
1090f98dd1bSBamvor Jian Zhang 				   GFP_KERNEL);
110*ca409160SBartosz Golaszewski 	if (!chip->lines) {
1110f98dd1bSBamvor Jian Zhang 		ret = -ENOMEM;
1120f98dd1bSBamvor Jian Zhang 		goto err;
1130f98dd1bSBamvor Jian Zhang 	}
114*ca409160SBartosz Golaszewski 
115*ca409160SBartosz Golaszewski 	ret = devm_gpiochip_add_data(dev, &chip->gc, chip);
1160f98dd1bSBamvor Jian Zhang 	if (ret)
1170f98dd1bSBamvor Jian Zhang 		goto err;
1180f98dd1bSBamvor Jian Zhang 
1190f98dd1bSBamvor Jian Zhang 	dev_info(dev, "gpio<%d..%d> add successful!", base, base + ngpio);
1200f98dd1bSBamvor Jian Zhang 	return 0;
121*ca409160SBartosz Golaszewski 
1220f98dd1bSBamvor Jian Zhang err:
1230f98dd1bSBamvor Jian Zhang 	dev_err(dev, "gpio<%d..%d> add failed!", base, base + ngpio);
1240f98dd1bSBamvor Jian Zhang 	return ret;
1250f98dd1bSBamvor Jian Zhang }
1260f98dd1bSBamvor Jian Zhang 
127*ca409160SBartosz Golaszewski static int gpio_mockup_probe(struct platform_device *pdev)
1280f98dd1bSBamvor Jian Zhang {
129*ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chips;
130364ac456SBartosz Golaszewski 	struct device *dev = &pdev->dev;
131364ac456SBartosz Golaszewski 	int ret, i, base, ngpio;
132ad6d8004SBartosz Golaszewski 	char *chip_name;
1330f98dd1bSBamvor Jian Zhang 
1340f98dd1bSBamvor Jian Zhang 	if (gpio_mockup_params_nr < 2)
1350f98dd1bSBamvor Jian Zhang 		return -EINVAL;
1360f98dd1bSBamvor Jian Zhang 
137*ca409160SBartosz Golaszewski 	chips = devm_kzalloc(dev,
138*ca409160SBartosz Golaszewski 			     sizeof(*chips) * (gpio_mockup_params_nr >> 1),
1390f98dd1bSBamvor Jian Zhang 			     GFP_KERNEL);
140*ca409160SBartosz Golaszewski 	if (!chips)
1410f98dd1bSBamvor Jian Zhang 		return -ENOMEM;
1420f98dd1bSBamvor Jian Zhang 
143*ca409160SBartosz Golaszewski 	platform_set_drvdata(pdev, chips);
1440f98dd1bSBamvor Jian Zhang 
1450f98dd1bSBamvor Jian Zhang 	for (i = 0; i < gpio_mockup_params_nr >> 1; i++) {
1460f98dd1bSBamvor Jian Zhang 		base = gpio_mockup_ranges[i * 2];
147*ca409160SBartosz Golaszewski 
1480f98dd1bSBamvor Jian Zhang 		if (base == -1)
1490f98dd1bSBamvor Jian Zhang 			ngpio = gpio_mockup_ranges[i * 2 + 1];
1500f98dd1bSBamvor Jian Zhang 		else
1510f98dd1bSBamvor Jian Zhang 			ngpio = gpio_mockup_ranges[i * 2 + 1] - base;
1520f98dd1bSBamvor Jian Zhang 
1530f98dd1bSBamvor Jian Zhang 		if (ngpio >= 0) {
154ad6d8004SBartosz Golaszewski 			chip_name = devm_kasprintf(dev, GFP_KERNEL,
155*ca409160SBartosz Golaszewski 						   "%s-%c", GPIO_MOCKUP_NAME,
156*ca409160SBartosz Golaszewski 						   gpio_mockup_name_start + i);
157ad6d8004SBartosz Golaszewski 			if (!chip_name)
158ad6d8004SBartosz Golaszewski 				return -ENOMEM;
159ad6d8004SBartosz Golaszewski 
160*ca409160SBartosz Golaszewski 			ret = gpio_mockup_add(dev, &chips[i],
1610f98dd1bSBamvor Jian Zhang 					      chip_name, base, ngpio);
1620f98dd1bSBamvor Jian Zhang 		} else {
1630f98dd1bSBamvor Jian Zhang 			ret = -1;
1640f98dd1bSBamvor Jian Zhang 		}
165*ca409160SBartosz Golaszewski 
1660f98dd1bSBamvor Jian Zhang 		if (ret) {
1670f98dd1bSBamvor Jian Zhang 			if (base < 0)
1680f98dd1bSBamvor Jian Zhang 				dev_err(dev, "gpio<%d..%d> add failed\n",
1690f98dd1bSBamvor Jian Zhang 					base, ngpio);
1700f98dd1bSBamvor Jian Zhang 			else
1710f98dd1bSBamvor Jian Zhang 				dev_err(dev, "gpio<%d..%d> add failed\n",
1720f98dd1bSBamvor Jian Zhang 					base, base + ngpio);
1730f98dd1bSBamvor Jian Zhang 
1740f98dd1bSBamvor Jian Zhang 			return ret;
1750f98dd1bSBamvor Jian Zhang 		}
1760f98dd1bSBamvor Jian Zhang 	}
1770f98dd1bSBamvor Jian Zhang 
1780f98dd1bSBamvor Jian Zhang 	return 0;
1790f98dd1bSBamvor Jian Zhang }
1800f98dd1bSBamvor Jian Zhang 
181*ca409160SBartosz Golaszewski static struct platform_driver gpio_mockup_driver = {
1820f98dd1bSBamvor Jian Zhang 	.driver = {
183*ca409160SBartosz Golaszewski 		.name = GPIO_MOCKUP_NAME,
1840f98dd1bSBamvor Jian Zhang 	},
185*ca409160SBartosz Golaszewski 	.probe = gpio_mockup_probe,
1860f98dd1bSBamvor Jian Zhang };
1870f98dd1bSBamvor Jian Zhang 
1880f98dd1bSBamvor Jian Zhang static struct platform_device *pdev;
1890f98dd1bSBamvor Jian Zhang static int __init mock_device_init(void)
1900f98dd1bSBamvor Jian Zhang {
1910f98dd1bSBamvor Jian Zhang 	int err;
1920f98dd1bSBamvor Jian Zhang 
193*ca409160SBartosz Golaszewski 	pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1);
1940f98dd1bSBamvor Jian Zhang 	if (!pdev)
1950f98dd1bSBamvor Jian Zhang 		return -ENOMEM;
1960f98dd1bSBamvor Jian Zhang 
1970f98dd1bSBamvor Jian Zhang 	err = platform_device_add(pdev);
1980f98dd1bSBamvor Jian Zhang 	if (err) {
1990f98dd1bSBamvor Jian Zhang 		platform_device_put(pdev);
2000f98dd1bSBamvor Jian Zhang 		return err;
2010f98dd1bSBamvor Jian Zhang 	}
2020f98dd1bSBamvor Jian Zhang 
203*ca409160SBartosz Golaszewski 	err = platform_driver_register(&gpio_mockup_driver);
2040f98dd1bSBamvor Jian Zhang 	if (err) {
2050f98dd1bSBamvor Jian Zhang 		platform_device_unregister(pdev);
2060f98dd1bSBamvor Jian Zhang 		return err;
2070f98dd1bSBamvor Jian Zhang 	}
2080f98dd1bSBamvor Jian Zhang 
2090f98dd1bSBamvor Jian Zhang 	return 0;
2100f98dd1bSBamvor Jian Zhang }
2110f98dd1bSBamvor Jian Zhang 
2120f98dd1bSBamvor Jian Zhang static void __exit mock_device_exit(void)
2130f98dd1bSBamvor Jian Zhang {
214*ca409160SBartosz Golaszewski 	platform_driver_unregister(&gpio_mockup_driver);
2150f98dd1bSBamvor Jian Zhang 	platform_device_unregister(pdev);
2160f98dd1bSBamvor Jian Zhang }
2170f98dd1bSBamvor Jian Zhang 
2180f98dd1bSBamvor Jian Zhang module_init(mock_device_init);
2190f98dd1bSBamvor Jian Zhang module_exit(mock_device_exit);
2200f98dd1bSBamvor Jian Zhang 
2210f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
2220f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
2230f98dd1bSBamvor Jian Zhang MODULE_DESCRIPTION("GPIO Testing driver");
2240f98dd1bSBamvor Jian Zhang MODULE_LICENSE("GPL v2");
225