diff options
Diffstat (limited to 'drivers/gpio')
| -rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-menz127.c | 9 | ||||
| -rw-r--r-- | drivers/gpio/gpio-pca953x.c | 3 | ||||
| -rw-r--r-- | drivers/gpio/gpio-pxa.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/gpio-tps65086.c | 139 | ||||
| -rw-r--r-- | drivers/gpio/gpio-xgene.c | 5 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.c | 133 | 
8 files changed, 250 insertions, 50 deletions
| diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4808e4657de5..5f3429f0bf46 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -898,6 +898,12 @@ config GPIO_TIMBERDALE  	---help---  	Add support for the GPIO IP in the timberdale FPGA. +config GPIO_TPS65086 +	tristate "TI TPS65086 GPO" +	depends on MFD_TPS65086 +	help +	  This driver supports the GPO on TI TPS65086x PMICs. +  config GPIO_TPS65218  	tristate "TPS65218 GPIO"  	depends on MFD_TPS65218 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 25decedba943..1e0b74f3b1ed 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_ARCH_TEGRA)	+= gpio-tegra.o  obj-$(CONFIG_GPIO_TIMBERDALE)	+= gpio-timberdale.o  obj-$(CONFIG_GPIO_PALMAS)	+= gpio-palmas.o  obj-$(CONFIG_GPIO_TPIC2810)	+= gpio-tpic2810.o +obj-$(CONFIG_GPIO_TPS65086)	+= gpio-tps65086.o  obj-$(CONFIG_GPIO_TPS65218)	+= gpio-tps65218.o  obj-$(CONFIG_GPIO_TPS6586X)	+= gpio-tps6586x.o  obj-$(CONFIG_GPIO_TPS65910)	+= gpio-tps65910.o diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index a68e199d579d..c5c9599a3a71 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -37,7 +37,6 @@ struct men_z127_gpio {  	void __iomem *reg_base;  	struct mcb_device *mdev;  	struct resource *mem; -	spinlock_t lock;  };  static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio, @@ -69,7 +68,7 @@ static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,  		debounce /= 50;  	} -	spin_lock(&priv->lock); +	spin_lock(&gc->bgpio_lock);  	db_en = readl(priv->reg_base + MEN_Z127_DBER); @@ -84,7 +83,7 @@ static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,  	writel(db_en, priv->reg_base + MEN_Z127_DBER);  	writel(db_cnt, priv->reg_base + GPIO_TO_DBCNT_REG(gpio)); -	spin_unlock(&priv->lock); +	spin_unlock(&gc->bgpio_lock);  	return 0;  } @@ -97,7 +96,7 @@ static int men_z127_request(struct gpio_chip *gc, unsigned gpio_pin)  	if (gpio_pin >= gc->ngpio)  		return -EINVAL; -	spin_lock(&priv->lock); +	spin_lock(&gc->bgpio_lock);  	od_en = readl(priv->reg_base + MEN_Z127_ODER);  	if (gpiochip_line_is_open_drain(gc, gpio_pin)) @@ -106,7 +105,7 @@ static int men_z127_request(struct gpio_chip *gc, unsigned gpio_pin)  		od_en &= ~BIT(gpio_pin);  	writel(od_en, priv->reg_base + MEN_Z127_ODER); -	spin_unlock(&priv->lock); +	spin_unlock(&gc->bgpio_lock);  	return 0;  } diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index d0d3065a7557..e66084c295fb 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -18,6 +18,7 @@  #include <linux/i2c.h>  #include <linux/platform_data/pca953x.h>  #include <linux/slab.h> +#include <asm/unaligned.h>  #include <linux/of_platform.h>  #include <linux/acpi.h> @@ -159,7 +160,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)  		switch (chip->chip_type) {  		case PCA953X_TYPE:  			ret = i2c_smbus_write_word_data(chip->client, -							reg << 1, (u16) *val); +			    reg << 1, cpu_to_le16(get_unaligned((u16 *)val)));  			break;  		case PCA957X_TYPE:  			ret = i2c_smbus_write_byte_data(chip->client, reg << 1, diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index b2b7b78664b8..76ac906b4d78 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -283,8 +283,8 @@ static int pxa_gpio_direction_output(struct gpio_chip *chip,  	writel_relaxed(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET));  	ret = pinctrl_gpio_direction_output(chip->base + offset); -	if (!ret) -		return 0; +	if (ret) +		return ret;  	spin_lock_irqsave(&gpio_lock, flags); diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c new file mode 100644 index 000000000000..8e25f01ac314 --- /dev/null +++ b/drivers/gpio/gpio-tps65086.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + *	Andrew F. Davis <afd@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License version 2 for more details. + * + * Based on the TPS65912 driver + */ + +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <linux/mfd/tps65086.h> + +struct tps65086_gpio { +	struct gpio_chip chip; +	struct tps65086 *tps; +}; + +static int tps65086_gpio_get_direction(struct gpio_chip *chip, +				       unsigned offset) +{ +	/* This device is output only */ +	return 0; +} + +static int tps65086_gpio_direction_input(struct gpio_chip *chip, +					 unsigned offset) +{ +	/* This device is output only */ +	return -EINVAL; +} + +static int tps65086_gpio_direction_output(struct gpio_chip *chip, +					  unsigned offset, int value) +{ +	struct tps65086_gpio *gpio = gpiochip_get_data(chip); + +	/* Set the initial value */ +	regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, +			   BIT(4 + offset), value ? BIT(4 + offset) : 0); + +	return 0; +} + +static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) +{ +	struct tps65086_gpio *gpio = gpiochip_get_data(chip); +	int ret, val; + +	ret = regmap_read(gpio->tps->regmap, TPS65086_GPOCTRL, &val); +	if (ret < 0) +		return ret; + +	return val & BIT(4 + offset); +} + +static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset, +			      int value) +{ +	struct tps65086_gpio *gpio = gpiochip_get_data(chip); + +	regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, +			   BIT(4 + offset), value ? BIT(4 + offset) : 0); +} + +static struct gpio_chip template_chip = { +	.label			= "tps65086-gpio", +	.owner			= THIS_MODULE, +	.get_direction		= tps65086_gpio_get_direction, +	.direction_input	= tps65086_gpio_direction_input, +	.direction_output	= tps65086_gpio_direction_output, +	.get			= tps65086_gpio_get, +	.set			= tps65086_gpio_set, +	.base			= -1, +	.ngpio			= 4, +	.can_sleep		= true, +}; + +static int tps65086_gpio_probe(struct platform_device *pdev) +{ +	struct tps65086_gpio *gpio; +	int ret; + +	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); +	if (!gpio) +		return -ENOMEM; + +	platform_set_drvdata(pdev, gpio); + +	gpio->tps = dev_get_drvdata(pdev->dev.parent); +	gpio->chip = template_chip; +	gpio->chip.parent = gpio->tps->dev; + +	ret = gpiochip_add_data(&gpio->chip, gpio); +	if (ret < 0) { +		dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int tps65086_gpio_remove(struct platform_device *pdev) +{ +	struct tps65086_gpio *gpio = platform_get_drvdata(pdev); + +	gpiochip_remove(&gpio->chip); + +	return 0; +} + +static const struct platform_device_id tps65086_gpio_id_table[] = { +	{ "tps65086-gpio", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table); + +static struct platform_driver tps65086_gpio_driver = { +	.driver = { +		.name = "tps65086-gpio", +	}, +	.probe = tps65086_gpio_probe, +	.remove = tps65086_gpio_remove, +	.id_table = tps65086_gpio_id_table, +}; +module_platform_driver(tps65086_gpio_driver); + +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("TPS65086 GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index c0aa387664bf..0dc916191689 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -173,6 +173,11 @@ static int xgene_gpio_probe(struct platform_device *pdev)  	}  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		err = -EINVAL; +		goto err; +	} +  	gpio->base = devm_ioremap_nocache(&pdev->dev, res->start,  							resource_size(res));  	if (!gpio->base) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 72065532c1c7..b747c76fd2b1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -68,6 +68,7 @@ LIST_HEAD(gpio_devices);  static void gpiochip_free_hogs(struct gpio_chip *chip);  static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); +static bool gpiolib_initialized;  static inline void desc_set_label(struct gpio_desc *d, const char *label)  { @@ -440,9 +441,63 @@ static void gpiodevice_release(struct device *dev)  	cdev_del(&gdev->chrdev);  	list_del(&gdev->list);  	ida_simple_remove(&gpio_ida, gdev->id); +	kfree(gdev->label); +	kfree(gdev->descs);  	kfree(gdev);  } +static int gpiochip_setup_dev(struct gpio_device *gdev) +{ +	int status; + +	cdev_init(&gdev->chrdev, &gpio_fileops); +	gdev->chrdev.owner = THIS_MODULE; +	gdev->chrdev.kobj.parent = &gdev->dev.kobj; +	gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); +	status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1); +	if (status < 0) +		chip_warn(gdev->chip, "failed to add char device %d:%d\n", +			  MAJOR(gpio_devt), gdev->id); +	else +		chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", +			 MAJOR(gpio_devt), gdev->id); +	status = device_add(&gdev->dev); +	if (status) +		goto err_remove_chardev; + +	status = gpiochip_sysfs_register(gdev); +	if (status) +		goto err_remove_device; + +	/* From this point, the .release() function cleans up gpio_device */ +	gdev->dev.release = gpiodevice_release; +	get_device(&gdev->dev); +	pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n", +		 __func__, gdev->base, gdev->base + gdev->ngpio - 1, +		 dev_name(&gdev->dev), gdev->chip->label ? : "generic"); + +	return 0; + +err_remove_device: +	device_del(&gdev->dev); +err_remove_chardev: +	cdev_del(&gdev->chrdev); +	return status; +} + +static void gpiochip_setup_devs(void) +{ +	struct gpio_device *gdev; +	int err; + +	list_for_each_entry(gdev, &gpio_devices, list) { +		err = gpiochip_setup_dev(gdev); +		if (err) +			pr_err("%s: Failed to initialize gpio device (%d)\n", +			       dev_name(&gdev->dev), err); +	} +} +  /**   * gpiochip_add_data() - register a gpio_chip   * @chip: the chip to register, with chip->base initialized @@ -457,6 +512,9 @@ static void gpiodevice_release(struct device *dev)   * the gpio framework's arch_initcall().  Otherwise sysfs initialization   * for GPIOs will fail rudely.   * + * gpiochip_add_data() must only be called after gpiolib initialization, + * ie after core_initcall(). + *   * If chip->base is negative, this requests dynamic assignment of   * a range of valid GPIOs.   */ @@ -504,8 +562,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)  	else  		gdev->owner = THIS_MODULE; -	gdev->descs = devm_kcalloc(&gdev->dev, chip->ngpio, -				   sizeof(gdev->descs[0]), GFP_KERNEL); +	gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);  	if (!gdev->descs) {  		status = -ENOMEM;  		goto err_free_gdev; @@ -514,16 +571,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)  	if (chip->ngpio == 0) {  		chip_err(chip, "tried to insert a GPIO chip with zero lines\n");  		status = -EINVAL; -		goto err_free_gdev; +		goto err_free_descs;  	}  	if (chip->label) -		gdev->label = devm_kstrdup(&gdev->dev, chip->label, GFP_KERNEL); +		gdev->label = kstrdup(chip->label, GFP_KERNEL);  	else -		gdev->label = devm_kstrdup(&gdev->dev, "unknown", GFP_KERNEL); +		gdev->label = kstrdup("unknown", GFP_KERNEL);  	if (!gdev->label) {  		status = -ENOMEM; -		goto err_free_gdev; +		goto err_free_descs;  	}  	gdev->ngpio = chip->ngpio; @@ -543,7 +600,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)  		if (base < 0) {  			status = base;  			spin_unlock_irqrestore(&gpio_lock, flags); -			goto err_free_gdev; +			goto err_free_label;  		}  		/*  		 * TODO: it should not be necessary to reflect the assigned @@ -558,7 +615,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)  	status = gpiodev_add_to_list(gdev);  	if (status) {  		spin_unlock_irqrestore(&gpio_lock, flags); -		goto err_free_gdev; +		goto err_free_label;  	}  	for (i = 0; i < chip->ngpio; i++) { @@ -596,39 +653,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)  	 * we get a device node entry in sysfs under  	 * /sys/bus/gpio/devices/gpiochipN/dev that can be used for  	 * coldplug of device nodes and other udev business. +	 * We can do this only if gpiolib has been initialized. +	 * Otherwise, defer until later.  	 */ -	cdev_init(&gdev->chrdev, &gpio_fileops); -	gdev->chrdev.owner = THIS_MODULE; -	gdev->chrdev.kobj.parent = &gdev->dev.kobj; -	gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); -	status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1); -	if (status < 0) -		chip_warn(chip, "failed to add char device %d:%d\n", -			  MAJOR(gpio_devt), gdev->id); -	else -		chip_dbg(chip, "added GPIO chardev (%d:%d)\n", -			 MAJOR(gpio_devt), gdev->id); -	status = device_add(&gdev->dev); -	if (status) -		goto err_remove_chardev; - -	status = gpiochip_sysfs_register(gdev); -	if (status) -		goto err_remove_device; - -	/* From this point, the .release() function cleans up gpio_device */ -	gdev->dev.release = gpiodevice_release; -	get_device(&gdev->dev); -	pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n", -		 __func__, gdev->base, gdev->base + gdev->ngpio - 1, -		 dev_name(&gdev->dev), chip->label ? : "generic"); - +	if (gpiolib_initialized) { +		status = gpiochip_setup_dev(gdev); +		if (status) +			goto err_remove_chip; +	}  	return 0; -err_remove_device: -	device_del(&gdev->dev); -err_remove_chardev: -	cdev_del(&gdev->chrdev);  err_remove_chip:  	acpi_gpiochip_remove(chip);  	gpiochip_free_hogs(chip); @@ -637,6 +671,10 @@ err_remove_from_list:  	spin_lock_irqsave(&gpio_lock, flags);  	list_del(&gdev->list);  	spin_unlock_irqrestore(&gpio_lock, flags); +err_free_label: +	kfree(gdev->label); +err_free_descs: +	kfree(gdev->descs);  err_free_gdev:  	ida_simple_remove(&gpio_ida, gdev->id);  	/* failures here can mean systems won't boot... */ @@ -2231,9 +2269,11 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,  	return desc;  } -static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, +static struct gpio_desc *acpi_find_gpio(struct device *dev, +					const char *con_id,  					unsigned int idx, -					enum gpio_lookup_flags *flags) +					enum gpiod_flags flags, +					enum gpio_lookup_flags *lookupflags)  {  	struct acpi_device *adev = ACPI_COMPANION(dev);  	struct acpi_gpio_info info; @@ -2264,10 +2304,16 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,  		desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);  		if (IS_ERR(desc))  			return desc; + +		if ((flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH) && +		    info.gpioint) { +			dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); +			return ERR_PTR(-ENOENT); +		}  	}  	if (info.polarity == GPIO_ACTIVE_LOW) -		*flags |= GPIO_ACTIVE_LOW; +		*lookupflags |= GPIO_ACTIVE_LOW;  	return desc;  } @@ -2530,7 +2576,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,  			desc = of_find_gpio(dev, con_id, idx, &lookupflags);  		} else if (ACPI_COMPANION(dev)) {  			dev_dbg(dev, "using ACPI for GPIO lookup\n"); -			desc = acpi_find_gpio(dev, con_id, idx, &lookupflags); +			desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags);  		}  	} @@ -2829,6 +2875,9 @@ static int __init gpiolib_dev_init(void)  	if (ret < 0) {  		pr_err("gpiolib: failed to allocate char dev region\n");  		bus_unregister(&gpio_bus_type); +	} else { +		gpiolib_initialized = true; +		gpiochip_setup_devs();  	}  	return ret;  } | 
