brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · d39c661 Raw
632 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * GPIO Testing Device Driver4 *5 * Copyright (C) 2014  Kamlakant Patel <kamlakant.patel@broadcom.com>6 * Copyright (C) 2015-2016  Bamvor Jian Zhang <bamv2005@gmail.com>7 * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>8 */9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <linux/cleanup.h>13#include <linux/debugfs.h>14#include <linux/device.h>15#include <linux/gpio/driver.h>16#include <linux/interrupt.h>17#include <linux/irq.h>18#include <linux/irq_sim.h>19#include <linux/irqdomain.h>20#include <linux/mod_devicetable.h>21#include <linux/module.h>22#include <linux/platform_device.h>23#include <linux/property.h>24#include <linux/seq_file.h>25#include <linux/slab.h>26#include <linux/string_helpers.h>27#include <linux/uaccess.h>28 29#define GPIO_MOCKUP_MAX_GC	1030/*31 * We're storing two values per chip: the GPIO base and the number32 * of GPIO lines.33 */34#define GPIO_MOCKUP_MAX_RANGES	(GPIO_MOCKUP_MAX_GC * 2)35/* Maximum of four properties + the sentinel. */36#define GPIO_MOCKUP_MAX_PROP	537 38/*39 * struct gpio_pin_status - structure describing a GPIO status40 * @dir:       Configures direction of gpio as "in" or "out"41 * @value:     Configures status of the gpio as 0(low) or 1(high)42 * @pull:      Configures the current pull of the GPIO as 0 (pull-down) or43 *             1 (pull-up)44 * @requested: Request status of this GPIO45 */46struct gpio_mockup_line_status {47	int dir;48	int value;49	int pull;50	bool requested;51};52 53struct gpio_mockup_chip {54	struct gpio_chip gc;55	struct gpio_mockup_line_status *lines;56	struct irq_domain *irq_sim_domain;57	struct dentry *dbg_dir;58	struct mutex lock;59};60 61struct gpio_mockup_dbgfs_private {62	struct gpio_mockup_chip *chip;63	unsigned int offset;64};65 66static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES];67static int gpio_mockup_num_ranges;68module_param_array(gpio_mockup_ranges, int, &gpio_mockup_num_ranges, 0400);69 70static bool gpio_mockup_named_lines;71module_param_named(gpio_mockup_named_lines,72		   gpio_mockup_named_lines, bool, 0400);73 74static struct dentry *gpio_mockup_dbg_dir;75 76static int gpio_mockup_range_base(unsigned int index)77{78	return gpio_mockup_ranges[index * 2];79}80 81static int gpio_mockup_range_ngpio(unsigned int index)82{83	return gpio_mockup_ranges[index * 2 + 1];84}85 86static int __gpio_mockup_get(struct gpio_mockup_chip *chip,87			     unsigned int offset)88{89	return chip->lines[offset].value;90}91 92static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)93{94	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);95	int val;96 97	scoped_guard(mutex, &chip->lock)98		val = __gpio_mockup_get(chip, offset);99 100	return val;101}102 103static int gpio_mockup_get_multiple(struct gpio_chip *gc,104				    unsigned long *mask, unsigned long *bits)105{106	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);107	unsigned int bit, val;108 109	scoped_guard(mutex, &chip->lock) {110		for_each_set_bit(bit, mask, gc->ngpio) {111			val = __gpio_mockup_get(chip, bit);112			__assign_bit(bit, bits, val);113		}114	}115 116	return 0;117}118 119static void __gpio_mockup_set(struct gpio_mockup_chip *chip,120			      unsigned int offset, int value)121{122	chip->lines[offset].value = !!value;123}124 125static void gpio_mockup_set(struct gpio_chip *gc,126			   unsigned int offset, int value)127{128	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);129 130	guard(mutex)(&chip->lock);131 132	__gpio_mockup_set(chip, offset, value);133}134 135static void gpio_mockup_set_multiple(struct gpio_chip *gc,136				     unsigned long *mask, unsigned long *bits)137{138	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);139	unsigned int bit;140 141	guard(mutex)(&chip->lock);142 143	for_each_set_bit(bit, mask, gc->ngpio)144		__gpio_mockup_set(chip, bit, test_bit(bit, bits));145}146 147static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,148				  unsigned int offset, int value)149{150	struct gpio_mockup_line_status *line = &chip->lines[offset];151	int curr, irq, irq_type, ret = 0;152 153	guard(mutex)(&chip->lock);154 155	if (line->requested && line->dir == GPIO_LINE_DIRECTION_IN) {156		curr = __gpio_mockup_get(chip, offset);157		if (curr == value)158			goto out;159 160		irq = irq_find_mapping(chip->irq_sim_domain, offset);161		if (!irq)162			/*163			 * This is fine - it just means, nobody is listening164			 * for interrupts on this line, otherwise165			 * irq_create_mapping() would have been called from166			 * the to_irq() callback.167			 */168			goto set_value;169 170		irq_type = irq_get_trigger_type(irq);171 172		if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||173		    (value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) {174			ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING,175						    true);176			if (ret)177				goto out;178		}179	}180 181set_value:182	/* Change the value unless we're actively driving the line. */183	if (!line->requested || line->dir == GPIO_LINE_DIRECTION_IN)184		__gpio_mockup_set(chip, offset, value);185 186out:187	chip->lines[offset].pull = value;188	return ret;189}190 191static int gpio_mockup_set_config(struct gpio_chip *gc,192				  unsigned int offset, unsigned long config)193{194	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);195 196	switch (pinconf_to_config_param(config)) {197	case PIN_CONFIG_BIAS_PULL_UP:198		return gpio_mockup_apply_pull(chip, offset, 1);199	case PIN_CONFIG_BIAS_PULL_DOWN:200		return gpio_mockup_apply_pull(chip, offset, 0);201	default:202		break;203	}204	return -ENOTSUPP;205}206 207static int gpio_mockup_dirout(struct gpio_chip *gc,208			      unsigned int offset, int value)209{210	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);211 212	scoped_guard(mutex, &chip->lock) {213		chip->lines[offset].dir = GPIO_LINE_DIRECTION_OUT;214		__gpio_mockup_set(chip, offset, value);215	}216 217	return 0;218}219 220static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)221{222	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);223 224	scoped_guard(mutex, &chip->lock)225		chip->lines[offset].dir = GPIO_LINE_DIRECTION_IN;226 227	return 0;228}229 230static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)231{232	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);233	int direction;234 235	scoped_guard(mutex, &chip->lock)236		direction = chip->lines[offset].dir;237 238	return direction;239}240 241static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)242{243	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);244 245	return irq_create_mapping(chip->irq_sim_domain, offset);246}247 248static int gpio_mockup_request(struct gpio_chip *gc, unsigned int offset)249{250	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);251 252	scoped_guard(mutex, &chip->lock)253		chip->lines[offset].requested = true;254 255	return 0;256}257 258static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)259{260	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);261 262	guard(mutex)(&chip->lock);263 264	chip->lines[offset].requested = false;265	__gpio_mockup_set(chip, offset, chip->lines[offset].pull);266}267 268static ssize_t gpio_mockup_debugfs_read(struct file *file,269					char __user *usr_buf,270					size_t size, loff_t *ppos)271{272	struct gpio_mockup_dbgfs_private *priv;273	struct gpio_mockup_chip *chip;274	struct seq_file *sfile;275	struct gpio_chip *gc;276	int val, cnt;277	char buf[3];278 279	if (*ppos != 0)280		return 0;281 282	sfile = file->private_data;283	priv = sfile->private;284	chip = priv->chip;285	gc = &chip->gc;286 287	val = gpio_mockup_get(gc, priv->offset);288	cnt = snprintf(buf, sizeof(buf), "%d\n", val);289 290	return simple_read_from_buffer(usr_buf, size, ppos, buf, cnt);291}292 293static ssize_t gpio_mockup_debugfs_write(struct file *file,294					 const char __user *usr_buf,295					 size_t size, loff_t *ppos)296{297	struct gpio_mockup_dbgfs_private *priv;298	int rv, val;299	struct seq_file *sfile;300 301	if (*ppos != 0)302		return -EINVAL;303 304	rv = kstrtoint_from_user(usr_buf, size, 0, &val);305	if (rv)306		return rv;307	if (val != 0 && val != 1)308		return -EINVAL;309 310	sfile = file->private_data;311	priv = sfile->private;312	rv = gpio_mockup_apply_pull(priv->chip, priv->offset, val);313	if (rv)314		return rv;315 316	return size;317}318 319static int gpio_mockup_debugfs_open(struct inode *inode, struct file *file)320{321	return single_open(file, NULL, inode->i_private);322}323 324/*325 * Each mockup chip is represented by a directory named after the chip's device326 * name under /sys/kernel/debug/gpio-mockup/. Each line is represented by327 * a file using the line's offset as the name under the chip's directory.328 *329 * Reading from the line's file yields the current *value*, writing to the330 * line's file changes the current *pull*. Default pull for mockup lines is331 * down.332 *333 * Examples:334 * - when a line pulled down is requested in output mode and driven high, its335 *   value will return to 0 once it's released336 * - when the line is requested in output mode and driven high, writing 0 to337 *   the corresponding debugfs file will change the pull to down but the338 *   reported value will still be 1 until the line is released339 * - line requested in input mode always reports the same value as its pull340 *   configuration341 * - when the line is requested in input mode and monitored for events, writing342 *   the same value to the debugfs file will be a noop, while writing the343 *   opposite value will generate a dummy interrupt with an appropriate edge344 */345static const struct file_operations gpio_mockup_debugfs_ops = {346	.owner = THIS_MODULE,347	.open = gpio_mockup_debugfs_open,348	.read = gpio_mockup_debugfs_read,349	.write = gpio_mockup_debugfs_write,350	.release = single_release,351};352 353static void gpio_mockup_debugfs_setup(struct device *dev,354				      struct gpio_mockup_chip *chip)355{356	struct gpio_mockup_dbgfs_private *priv;357	struct gpio_chip *gc;358	const char *devname;359	char *name;360	int i;361 362	gc = &chip->gc;363 364	/*365	 * There can only be a single GPIO device per platform device in366	 * gpio-mockup so using device_find_any_child() is OK.367	 */368	struct device *child __free(put_device) = device_find_any_child(dev);369	if (!child)370		return;371 372	devname = dev_name(child);373	chip->dbg_dir = debugfs_create_dir(devname, gpio_mockup_dbg_dir);374 375	for (i = 0; i < gc->ngpio; i++) {376		name = devm_kasprintf(dev, GFP_KERNEL, "%d", i);377		if (!name)378			return;379 380		priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);381		if (!priv)382			return;383 384		priv->chip = chip;385		priv->offset = i;386 387		debugfs_create_file(name, 0600, chip->dbg_dir, priv,388				    &gpio_mockup_debugfs_ops);389	}390}391 392static void gpio_mockup_debugfs_cleanup(void *data)393{394	struct gpio_mockup_chip *chip = data;395 396	debugfs_remove_recursive(chip->dbg_dir);397}398 399static void gpio_mockup_dispose_mappings(void *data)400{401	struct gpio_mockup_chip *chip = data;402	struct gpio_chip *gc = &chip->gc;403	int i, irq;404 405	for (i = 0; i < gc->ngpio; i++) {406		irq = irq_find_mapping(chip->irq_sim_domain, i);407		if (irq)408			irq_dispose_mapping(irq);409	}410}411 412static int gpio_mockup_probe(struct platform_device *pdev)413{414	struct gpio_mockup_chip *chip;415	struct gpio_chip *gc;416	struct device *dev;417	const char *name;418	int rv, base, i;419	u16 ngpio;420 421	dev = &pdev->dev;422 423	rv = device_property_read_u32(dev, "gpio-base", &base);424	if (rv)425		base = -1;426 427	rv = device_property_read_u16(dev, "nr-gpios", &ngpio);428	if (rv)429		return rv;430 431	rv = device_property_read_string(dev, "chip-label", &name);432	if (rv)433		name = dev_name(dev);434 435	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);436	if (!chip)437		return -ENOMEM;438 439	mutex_init(&chip->lock);440 441	gc = &chip->gc;442	gc->base = base;443	gc->ngpio = ngpio;444	gc->label = name;445	gc->owner = THIS_MODULE;446	gc->parent = dev;447	gc->get = gpio_mockup_get;448	gc->set = gpio_mockup_set;449	gc->get_multiple = gpio_mockup_get_multiple;450	gc->set_multiple = gpio_mockup_set_multiple;451	gc->direction_output = gpio_mockup_dirout;452	gc->direction_input = gpio_mockup_dirin;453	gc->get_direction = gpio_mockup_get_direction;454	gc->set_config = gpio_mockup_set_config;455	gc->to_irq = gpio_mockup_to_irq;456	gc->request = gpio_mockup_request;457	gc->free = gpio_mockup_free;458 459	chip->lines = devm_kcalloc(dev, gc->ngpio,460				   sizeof(*chip->lines), GFP_KERNEL);461	if (!chip->lines)462		return -ENOMEM;463 464	for (i = 0; i < gc->ngpio; i++)465		chip->lines[i].dir = GPIO_LINE_DIRECTION_IN;466 467	chip->irq_sim_domain = devm_irq_domain_create_sim(dev, NULL,468							  gc->ngpio);469	if (IS_ERR(chip->irq_sim_domain))470		return PTR_ERR(chip->irq_sim_domain);471 472	rv = devm_add_action_or_reset(dev, gpio_mockup_dispose_mappings, chip);473	if (rv)474		return rv;475 476	rv = devm_gpiochip_add_data(dev, &chip->gc, chip);477	if (rv)478		return rv;479 480	gpio_mockup_debugfs_setup(dev, chip);481 482	return devm_add_action_or_reset(dev, gpio_mockup_debugfs_cleanup, chip);483}484 485static const struct of_device_id gpio_mockup_of_match[] = {486	{ .compatible = "gpio-mockup", },487	{},488};489MODULE_DEVICE_TABLE(of, gpio_mockup_of_match);490 491static struct platform_driver gpio_mockup_driver = {492	.driver = {493		.name = "gpio-mockup",494		.of_match_table = gpio_mockup_of_match,495	},496	.probe = gpio_mockup_probe,497};498 499static struct platform_device *gpio_mockup_pdevs[GPIO_MOCKUP_MAX_GC];500 501static void gpio_mockup_unregister_pdevs(void)502{503	struct platform_device *pdev;504	struct fwnode_handle *fwnode;505	int i;506 507	for (i = 0; i < GPIO_MOCKUP_MAX_GC; i++) {508		pdev = gpio_mockup_pdevs[i];509		if (!pdev)510			continue;511 512		fwnode = dev_fwnode(&pdev->dev);513		platform_device_unregister(pdev);514		fwnode_remove_software_node(fwnode);515	}516}517 518static int __init gpio_mockup_register_chip(int idx)519{520	struct property_entry properties[GPIO_MOCKUP_MAX_PROP];521	struct platform_device_info pdevinfo;522	struct platform_device *pdev;523	struct fwnode_handle *fwnode;524	char **line_names = NULL;525	char chip_label[32];526	int prop = 0, base;527	u16 ngpio;528 529	memset(properties, 0, sizeof(properties));530	memset(&pdevinfo, 0, sizeof(pdevinfo));531 532	snprintf(chip_label, sizeof(chip_label), "gpio-mockup-%c", idx + 'A');533	properties[prop++] = PROPERTY_ENTRY_STRING("chip-label", chip_label);534 535	base = gpio_mockup_range_base(idx);536	if (base >= 0)537		properties[prop++] = PROPERTY_ENTRY_U32("gpio-base", base);538 539	ngpio = base < 0 ? gpio_mockup_range_ngpio(idx)540			 : gpio_mockup_range_ngpio(idx) - base;541	properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);542 543	if (gpio_mockup_named_lines) {544		line_names = kasprintf_strarray(GFP_KERNEL, chip_label, ngpio);545		if (!line_names)546			return -ENOMEM;547 548		properties[prop++] = PROPERTY_ENTRY_STRING_ARRAY_LEN(549					"gpio-line-names", line_names, ngpio);550	}551 552	fwnode = fwnode_create_software_node(properties, NULL);553	if (IS_ERR(fwnode)) {554		kfree_strarray(line_names, ngpio);555		return PTR_ERR(fwnode);556	}557 558	pdevinfo.name = "gpio-mockup";559	pdevinfo.id = idx;560	pdevinfo.fwnode = fwnode;561 562	pdev = platform_device_register_full(&pdevinfo);563	kfree_strarray(line_names, ngpio);564	if (IS_ERR(pdev)) {565		fwnode_remove_software_node(fwnode);566		pr_err("error registering device");567		return PTR_ERR(pdev);568	}569 570	gpio_mockup_pdevs[idx] = pdev;571 572	return 0;573}574 575static int __init gpio_mockup_init(void)576{577	int i, num_chips, err;578 579	if ((gpio_mockup_num_ranges % 2) ||580	    (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES))581		return -EINVAL;582 583	/* Each chip is described by two values. */584	num_chips = gpio_mockup_num_ranges / 2;585 586	/*587	 * The second value in the <base GPIO - number of GPIOS> pair must588	 * always be greater than 0.589	 */590	for (i = 0; i < num_chips; i++) {591		if (gpio_mockup_range_ngpio(i) < 0)592			return -EINVAL;593	}594 595	gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL);596 597	err = platform_driver_register(&gpio_mockup_driver);598	if (err) {599		pr_err("error registering platform driver\n");600		debugfs_remove_recursive(gpio_mockup_dbg_dir);601		return err;602	}603 604	for (i = 0; i < num_chips; i++) {605		err = gpio_mockup_register_chip(i);606		if (err) {607			platform_driver_unregister(&gpio_mockup_driver);608			gpio_mockup_unregister_pdevs();609			debugfs_remove_recursive(gpio_mockup_dbg_dir);610			return err;611		}612	}613 614	return 0;615}616 617static void __exit gpio_mockup_exit(void)618{619	gpio_mockup_unregister_pdevs();620	debugfs_remove_recursive(gpio_mockup_dbg_dir);621	platform_driver_unregister(&gpio_mockup_driver);622}623 624module_init(gpio_mockup_init);625module_exit(gpio_mockup_exit);626 627MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");628MODULE_AUTHOR("Bamvor Jian Zhang <bamv2005@gmail.com>");629MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");630MODULE_DESCRIPTION("GPIO Testing driver");631MODULE_LICENSE("GPL v2");632