xref: /openbmc/linux/drivers/gpio/gpio-mockup.c (revision e4ba07bf8d1481e440d1f49652c96567f9560fdc)
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 
19ca409160SBartosz Golaszewski #define GPIO_MOCKUP_NAME	"gpio-mockup"
20ca409160SBartosz Golaszewski #define	GPIO_MOCKUP_MAX_GC	10
210f98dd1bSBamvor Jian Zhang 
22ca409160SBartosz Golaszewski enum {
23ca409160SBartosz Golaszewski 	DIR_IN = 0,
24ca409160SBartosz 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  */
32ca409160SBartosz Golaszewski struct gpio_mockup_line_status {
33ca409160SBartosz Golaszewski 	int dir;
340f98dd1bSBamvor Jian Zhang 	bool value;
350f98dd1bSBamvor Jian Zhang };
360f98dd1bSBamvor Jian Zhang 
37ca409160SBartosz Golaszewski struct gpio_mockup_chip {
380f98dd1bSBamvor Jian Zhang 	struct gpio_chip gc;
39ca409160SBartosz Golaszewski 	struct gpio_mockup_line_status *lines;
400f98dd1bSBamvor Jian Zhang };
410f98dd1bSBamvor Jian Zhang 
42ca409160SBartosz 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 
46ca409160SBartosz Golaszewski static const char gpio_mockup_name_start = 'A';
470f98dd1bSBamvor Jian Zhang 
48ca409160SBartosz Golaszewski static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)
490f98dd1bSBamvor Jian Zhang {
50ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
510f98dd1bSBamvor Jian Zhang 
52ca409160SBartosz Golaszewski 	return chip->lines[offset].value;
530f98dd1bSBamvor Jian Zhang }
540f98dd1bSBamvor Jian Zhang 
55ca409160SBartosz Golaszewski static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset,
560f98dd1bSBamvor Jian Zhang 			    int value)
570f98dd1bSBamvor Jian Zhang {
58ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
590f98dd1bSBamvor Jian Zhang 
60ca409160SBartosz Golaszewski 	chip->lines[offset].value = !!value;
610f98dd1bSBamvor Jian Zhang }
620f98dd1bSBamvor Jian Zhang 
63ca409160SBartosz Golaszewski static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset,
640f98dd1bSBamvor Jian Zhang 			      int value)
650f98dd1bSBamvor Jian Zhang {
66ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
670f98dd1bSBamvor Jian Zhang 
68ca409160SBartosz Golaszewski 	gpio_mockup_set(gc, offset, value);
69ca409160SBartosz Golaszewski 	chip->lines[offset].dir = DIR_OUT;
70ca409160SBartosz Golaszewski 
710f98dd1bSBamvor Jian Zhang 	return 0;
720f98dd1bSBamvor Jian Zhang }
730f98dd1bSBamvor Jian Zhang 
74ca409160SBartosz Golaszewski static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
750f98dd1bSBamvor Jian Zhang {
76ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
770f98dd1bSBamvor Jian Zhang 
78ca409160SBartosz Golaszewski 	chip->lines[offset].dir = DIR_IN;
79ca409160SBartosz Golaszewski 
800f98dd1bSBamvor Jian Zhang 	return 0;
810f98dd1bSBamvor Jian Zhang }
820f98dd1bSBamvor Jian Zhang 
83ca409160SBartosz Golaszewski static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
840f98dd1bSBamvor Jian Zhang {
85ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
860f98dd1bSBamvor Jian Zhang 
87ca409160SBartosz Golaszewski 	return chip->lines[offset].dir;
880f98dd1bSBamvor Jian Zhang }
890f98dd1bSBamvor Jian Zhang 
90ca409160SBartosz Golaszewski static int gpio_mockup_add(struct device *dev,
91ca409160SBartosz Golaszewski 			   struct gpio_mockup_chip *chip,
920f98dd1bSBamvor Jian Zhang 			   const char *name, int base, int ngpio)
930f98dd1bSBamvor Jian Zhang {
94ca409160SBartosz Golaszewski 	struct gpio_chip *gc = &chip->gc;
950f98dd1bSBamvor Jian Zhang 
96ca409160SBartosz Golaszewski 	gc->base = base;
97ca409160SBartosz Golaszewski 	gc->ngpio = ngpio;
98ca409160SBartosz Golaszewski 	gc->label = name;
99ca409160SBartosz Golaszewski 	gc->owner = THIS_MODULE;
100ca409160SBartosz Golaszewski 	gc->parent = dev;
101ca409160SBartosz Golaszewski 	gc->get = gpio_mockup_get;
102ca409160SBartosz Golaszewski 	gc->set = gpio_mockup_set;
103ca409160SBartosz Golaszewski 	gc->direction_output = gpio_mockup_dirout;
104ca409160SBartosz Golaszewski 	gc->direction_input = gpio_mockup_dirin;
105ca409160SBartosz Golaszewski 	gc->get_direction = gpio_mockup_get_direction;
106ca409160SBartosz Golaszewski 
107ca409160SBartosz Golaszewski 	chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
1080f98dd1bSBamvor Jian Zhang 				   GFP_KERNEL);
109*e4ba07bfSBartosz Golaszewski 	if (!chip->lines)
110*e4ba07bfSBartosz Golaszewski 		return -ENOMEM;
111ca409160SBartosz Golaszewski 
112*e4ba07bfSBartosz Golaszewski 	return devm_gpiochip_add_data(dev, &chip->gc, chip);
1130f98dd1bSBamvor Jian Zhang }
1140f98dd1bSBamvor Jian Zhang 
115ca409160SBartosz Golaszewski static int gpio_mockup_probe(struct platform_device *pdev)
1160f98dd1bSBamvor Jian Zhang {
117ca409160SBartosz Golaszewski 	struct gpio_mockup_chip *chips;
118364ac456SBartosz Golaszewski 	struct device *dev = &pdev->dev;
119364ac456SBartosz Golaszewski 	int ret, i, base, ngpio;
120ad6d8004SBartosz Golaszewski 	char *chip_name;
1210f98dd1bSBamvor Jian Zhang 
1220f98dd1bSBamvor Jian Zhang 	if (gpio_mockup_params_nr < 2)
1230f98dd1bSBamvor Jian Zhang 		return -EINVAL;
1240f98dd1bSBamvor Jian Zhang 
125ca409160SBartosz Golaszewski 	chips = devm_kzalloc(dev,
126ca409160SBartosz Golaszewski 			     sizeof(*chips) * (gpio_mockup_params_nr >> 1),
1270f98dd1bSBamvor Jian Zhang 			     GFP_KERNEL);
128ca409160SBartosz Golaszewski 	if (!chips)
1290f98dd1bSBamvor Jian Zhang 		return -ENOMEM;
1300f98dd1bSBamvor Jian Zhang 
131ca409160SBartosz Golaszewski 	platform_set_drvdata(pdev, chips);
1320f98dd1bSBamvor Jian Zhang 
1330f98dd1bSBamvor Jian Zhang 	for (i = 0; i < gpio_mockup_params_nr >> 1; i++) {
1340f98dd1bSBamvor Jian Zhang 		base = gpio_mockup_ranges[i * 2];
135ca409160SBartosz Golaszewski 
1360f98dd1bSBamvor Jian Zhang 		if (base == -1)
1370f98dd1bSBamvor Jian Zhang 			ngpio = gpio_mockup_ranges[i * 2 + 1];
1380f98dd1bSBamvor Jian Zhang 		else
1390f98dd1bSBamvor Jian Zhang 			ngpio = gpio_mockup_ranges[i * 2 + 1] - base;
1400f98dd1bSBamvor Jian Zhang 
1410f98dd1bSBamvor Jian Zhang 		if (ngpio >= 0) {
142ad6d8004SBartosz Golaszewski 			chip_name = devm_kasprintf(dev, GFP_KERNEL,
143ca409160SBartosz Golaszewski 						   "%s-%c", GPIO_MOCKUP_NAME,
144ca409160SBartosz Golaszewski 						   gpio_mockup_name_start + i);
145ad6d8004SBartosz Golaszewski 			if (!chip_name)
146ad6d8004SBartosz Golaszewski 				return -ENOMEM;
147ad6d8004SBartosz Golaszewski 
148ca409160SBartosz Golaszewski 			ret = gpio_mockup_add(dev, &chips[i],
1490f98dd1bSBamvor Jian Zhang 					      chip_name, base, ngpio);
1500f98dd1bSBamvor Jian Zhang 		} else {
1510f98dd1bSBamvor Jian Zhang 			ret = -1;
1520f98dd1bSBamvor Jian Zhang 		}
153ca409160SBartosz Golaszewski 
1540f98dd1bSBamvor Jian Zhang 		if (ret) {
1550f98dd1bSBamvor Jian Zhang 			dev_err(dev, "gpio<%d..%d> add failed\n",
156*e4ba07bfSBartosz Golaszewski 				base, base < 0 ? ngpio : base + ngpio);
1570f98dd1bSBamvor Jian Zhang 
1580f98dd1bSBamvor Jian Zhang 			return ret;
1590f98dd1bSBamvor Jian Zhang 		}
160*e4ba07bfSBartosz Golaszewski 
161*e4ba07bfSBartosz Golaszewski 		dev_info(dev, "gpio<%d..%d> add successful!",
162*e4ba07bfSBartosz Golaszewski 			 base, base + ngpio);
1630f98dd1bSBamvor Jian Zhang 	}
1640f98dd1bSBamvor Jian Zhang 
1650f98dd1bSBamvor Jian Zhang 	return 0;
1660f98dd1bSBamvor Jian Zhang }
1670f98dd1bSBamvor Jian Zhang 
168ca409160SBartosz Golaszewski static struct platform_driver gpio_mockup_driver = {
1690f98dd1bSBamvor Jian Zhang 	.driver = {
170ca409160SBartosz Golaszewski 		.name = GPIO_MOCKUP_NAME,
1710f98dd1bSBamvor Jian Zhang 	},
172ca409160SBartosz Golaszewski 	.probe = gpio_mockup_probe,
1730f98dd1bSBamvor Jian Zhang };
1740f98dd1bSBamvor Jian Zhang 
1750f98dd1bSBamvor Jian Zhang static struct platform_device *pdev;
1760f98dd1bSBamvor Jian Zhang static int __init mock_device_init(void)
1770f98dd1bSBamvor Jian Zhang {
1780f98dd1bSBamvor Jian Zhang 	int err;
1790f98dd1bSBamvor Jian Zhang 
180ca409160SBartosz Golaszewski 	pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1);
1810f98dd1bSBamvor Jian Zhang 	if (!pdev)
1820f98dd1bSBamvor Jian Zhang 		return -ENOMEM;
1830f98dd1bSBamvor Jian Zhang 
1840f98dd1bSBamvor Jian Zhang 	err = platform_device_add(pdev);
1850f98dd1bSBamvor Jian Zhang 	if (err) {
1860f98dd1bSBamvor Jian Zhang 		platform_device_put(pdev);
1870f98dd1bSBamvor Jian Zhang 		return err;
1880f98dd1bSBamvor Jian Zhang 	}
1890f98dd1bSBamvor Jian Zhang 
190ca409160SBartosz Golaszewski 	err = platform_driver_register(&gpio_mockup_driver);
1910f98dd1bSBamvor Jian Zhang 	if (err) {
1920f98dd1bSBamvor Jian Zhang 		platform_device_unregister(pdev);
1930f98dd1bSBamvor Jian Zhang 		return err;
1940f98dd1bSBamvor Jian Zhang 	}
1950f98dd1bSBamvor Jian Zhang 
1960f98dd1bSBamvor Jian Zhang 	return 0;
1970f98dd1bSBamvor Jian Zhang }
1980f98dd1bSBamvor Jian Zhang 
1990f98dd1bSBamvor Jian Zhang static void __exit mock_device_exit(void)
2000f98dd1bSBamvor Jian Zhang {
201ca409160SBartosz Golaszewski 	platform_driver_unregister(&gpio_mockup_driver);
2020f98dd1bSBamvor Jian Zhang 	platform_device_unregister(pdev);
2030f98dd1bSBamvor Jian Zhang }
2040f98dd1bSBamvor Jian Zhang 
2050f98dd1bSBamvor Jian Zhang module_init(mock_device_init);
2060f98dd1bSBamvor Jian Zhang module_exit(mock_device_exit);
2070f98dd1bSBamvor Jian Zhang 
2080f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
2090f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
2100f98dd1bSBamvor Jian Zhang MODULE_DESCRIPTION("GPIO Testing driver");
2110f98dd1bSBamvor Jian Zhang MODULE_LICENSE("GPL v2");
212