diff options
Diffstat (limited to 'drivers/gpio')
33 files changed, 1573 insertions, 157 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d786061..98dd47a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -250,7 +250,7 @@ config GPIO_LOONGSON driver for GPIO functionality on Loongson-2F/3A/3B processors. config GPIO_LPC18XX - bool "NXP LPC18XX/43XX GPIO support" + tristate "NXP LPC18XX/43XX GPIO support" default y if ARCH_LPC18XX depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST) help @@ -874,6 +874,15 @@ config GPIO_LP3943 LP3943 can be used as a GPIO expander which provides up to 16 GPIOs. Open drain outputs are required for this usage. +config GPIO_MAX77620 + tristate "GPIO support for PMIC MAX77620 and MAX20024" + depends on MFD_MAX77620 + help + GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor. + MAX77620 PMIC has 8 pins that can be configured as GPIOs. The + driver also provides interrupt support for each of the gpios. + Say yes here to enable the max77620 to be used as gpio controller. + config GPIO_MSIC bool "Intel MSIC mixed signal gpio support" depends on MFD_INTEL_MSIC @@ -1029,11 +1038,18 @@ config GPIO_BT8XX If unsure, say N. config GPIO_INTEL_MID - bool "Intel Mid GPIO support" - depends on X86 + bool "Intel MID GPIO support" + depends on X86_INTEL_MID + select GPIOLIB_IRQCHIP + help + Say Y here to support Intel MID GPIO. + +config GPIO_MERRIFIELD + tristate "Intel Merrifield GPIO support" + depends on X86_INTEL_MID select GPIOLIB_IRQCHIP help - Say Y here to support Intel Mid GPIO. + Say Y here to support Intel Merrifield GPIO. config GPIO_ML_IOH tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 991598e..2a035ed 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -61,8 +61,10 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o +obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o +obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 80f9ddf..a6607fa 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -35,13 +35,8 @@ struct gen_74x164_chip { static int __gen_74x164_write_config(struct gen_74x164_chip *chip) { - struct spi_transfer xfer = { - .tx_buf = chip->buffer, - .len = chip->registers, - }; - - return spi_sync_transfer(to_spi_device(chip->gpio_chip.parent), - &xfer, 1); + return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer, + chip->registers); } static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index 5a69025..52fd63f 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -20,8 +20,12 @@ static int clps711x_gpio_probe(struct platform_device *pdev) void __iomem *dat, *dir; struct gpio_chip *gc; struct resource *res; - int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id; + int err, id; + if (!np) + return -ENODEV; + + id = of_alias_get_id(np, "gpio"); if ((id < 0) || (id > 4)) return -ENODEV; @@ -63,7 +67,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev) break; } - gc->base = id * 8; + gc->base = -1; gc->owner = THIS_MODULE; platform_set_drvdata(pdev, gc); @@ -71,7 +75,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev) } static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = { - { .compatible = "cirrus,clps711x-gpio" }, + { .compatible = "cirrus,ep7209-gpio" }, { } }; MODULE_DEVICE_TABLE(of, clps711x_gpio_ids); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 34779bb..6193f62 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -486,6 +486,7 @@ dwapb_gpio_get_pdata(struct device *dev) pp->idx >= DWAPB_MAX_PORTS) { dev_err(dev, "missing/invalid port index for port%d\n", i); + fwnode_handle_put(fwnode); return ERR_PTR(-EINVAL); } diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 05aa538..600be84 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -125,6 +125,7 @@ static inline void superio_exit(int base) * GPIO chip. */ +static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset); static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset); static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset); static int f7188x_gpio_direction_out(struct gpio_chip *chip, @@ -139,6 +140,7 @@ static int f7188x_gpio_set_single_ended(struct gpio_chip *gc, .chip = { \ .label = DRVNAME, \ .owner = THIS_MODULE, \ + .get_direction = f7188x_gpio_get_direction, \ .direction_input = f7188x_gpio_direction_in, \ .get = f7188x_gpio_get, \ .direction_output = f7188x_gpio_direction_out, \ @@ -209,6 +211,26 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = { F7188X_GPIO_BANK(80, 8, 0x88), }; +static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + int err; + struct f7188x_gpio_bank *bank = + container_of(chip, struct f7188x_gpio_bank, chip); + struct f7188x_sio *sio = bank->data->sio; + u8 dir; + + err = superio_enter(sio->addr); + if (err) + return err; + superio_select(sio->addr, SIO_LD_GPIO); + + dir = superio_inb(sio->addr, gpio_dir(bank->regbase)); + + superio_exit(sio->addr); + + return !(dir & 1 << offset); +} + static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { int err; diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index cdaba13..164de64 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -1,7 +1,7 @@ /* * Intel MID GPIO driver * - * Copyright (c) 2008-2014 Intel Corporation. + * Copyright (c) 2008-2014,2016 Intel Corporation. * * 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 @@ -17,21 +17,20 @@ * Moorestown platform Langwell chip. * Medfield platform Penwell chip. * Clovertrail platform Cloverview chip. - * Merrifield platform Tangier chip. */ -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/platform_device.h> -#include <linux/kernel.h> #include <linux/delay.h> -#include <linux/stddef.h> -#include <linux/interrupt.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/gpio/driver.h> -#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/stddef.h> #define INTEL_MID_IRQ_TYPE_EDGE (1 << 0) #define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1) @@ -64,10 +63,6 @@ enum GPIO_REG { /* intel_mid gpio driver data */ struct intel_mid_gpio_ddata { u16 ngpio; /* number of gpio pins */ - u32 gplr_offset; /* offset of first GPLR register from base */ - u32 flis_base; /* base address of FLIS registers */ - u32 flis_len; /* length of FLIS registers */ - u32 (*get_flis_offset)(int gpio); u32 chip_irq_type; /* chip interrupt type */ }; @@ -252,15 +247,6 @@ static const struct intel_mid_gpio_ddata gpio_cloverview_core = { .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE, }; -static const struct intel_mid_gpio_ddata gpio_tangier = { - .ngpio = 192, - .gplr_offset = 4, - .flis_base = 0xff0c0000, - .flis_len = 0x8000, - .get_flis_offset = NULL, - .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE, -}; - static const struct pci_device_id intel_gpio_ids[] = { { /* Lincroft */ @@ -287,11 +273,6 @@ static const struct pci_device_id intel_gpio_ids[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7), .driver_data = (kernel_ulong_t)&gpio_cloverview_core, }, - { - /* Tangier */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199), - .driver_data = (kernel_ulong_t)&gpio_tangier, - }, { 0 } }; MODULE_DEVICE_TABLE(pci, intel_gpio_ids); @@ -401,7 +382,7 @@ static int intel_gpio_probe(struct pci_dev *pdev, spin_lock_init(&priv->lock); pci_set_drvdata(pdev, priv); - retval = gpiochip_add_data(&priv->chip, priv); + retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); if (retval) { dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); return retval; diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 9df015e..fbd393b 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -383,7 +383,6 @@ static int lp_gpio_probe(struct platform_device *pdev) handle_simple_irq, IRQ_TYPE_NONE); if (ret) { dev_err(dev, "failed to add irqchip\n"); - gpiochip_remove(gc); return ret; } diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c new file mode 100644 index 0000000..b46b436 --- /dev/null +++ b/drivers/gpio/gpio-max77620.c @@ -0,0 +1,315 @@ +/* + * MAXIM MAX77620 GPIO driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/mfd/max77620.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define GPIO_REG_ADDR(offset) (MAX77620_REG_GPIO0 + offset) + +struct max77620_gpio { + struct gpio_chip gpio_chip; + struct regmap *rmap; + struct device *dev; + int gpio_irq; + int irq_base; + int gpio_base; +}; + +static const struct regmap_irq max77620_gpio_irqs[] = { + [0] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 0, + }, + [1] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 1, + }, + [2] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 2, + }, + [3] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 3, + }, + [4] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 4, + }, + [5] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 5, + }, + [6] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 6, + }, + [7] = { + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, + .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, + .reg_offset = 0, + .type_reg_offset = 7, + }, +}; + +static struct regmap_irq_chip max77620_gpio_irq_chip = { + .name = "max77620-gpio", + .irqs = max77620_gpio_irqs, + .num_irqs = ARRAY_SIZE(max77620_gpio_irqs), + .num_regs = 1, + .num_type_reg = 8, + .irq_reg_stride = 1, + .type_reg_stride = 1, + .status_base = MAX77620_REG_IRQ_LVL2_GPIO, + .type_base = MAX77620_REG_GPIO0, +}; + +static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + int ret; + + ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_DIR_MASK, + MAX77620_CNFG_GPIO_DIR_INPUT); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); + + return ret; +} + +static int max77620_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + unsigned int val; + int ret; + + ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val); + if (ret < 0) { + dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret); + return ret; + } + + if (val & MAX77620_CNFG_GPIO_DIR_MASK) + return !!(val & MAX77620_CNFG_GPIO_INPUT_VAL_MASK); + else + return !!(val & MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK); +} + +static int max77620_gpio_dir_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + u8 val; + int ret; + + val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : + MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; + + ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); + if (ret < 0) { + dev_err(mgpio->dev, "CNFG_GPIOx val update failed: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_DIR_MASK, + MAX77620_CNFG_GPIO_DIR_OUTPUT); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); + + return ret; +} + +static int max77620_gpio_set_debounce(struct gpio_chip *gc, + unsigned int offset, + unsigned int debounce) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + u8 val; + int ret; + + switch (debounce) { + case 0: + val = MAX77620_CNFG_GPIO_DBNC_None; + break; + case 1 ... 8: + val = MAX77620_CNFG_GPIO_DBNC_8ms; + break; + case 9 ... 16: + val = MAX77620_CNFG_GPIO_DBNC_16ms; + break; + case 17 ... 32: + val = MAX77620_CNFG_GPIO_DBNC_32ms; + break; + default: + dev_err(mgpio->dev, "Illegal value %u\n", debounce); + return -EINVAL; + } + + ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_DBNC_MASK, val); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIOx_DBNC update failed: %d\n", ret); + + return ret; +} + +static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + u8 val; + int ret; + + val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : + MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; + + ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret); +} + +static int max77620_gpio_set_single_ended(struct gpio_chip *gc, + unsigned int offset, + enum single_ended_mode mode) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + + switch (mode) { + case LINE_MODE_OPEN_DRAIN: + return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_DRV_MASK, + MAX77620_CNFG_GPIO_DRV_OPENDRAIN); + case LINE_MODE_PUSH_PULL: + return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_DRV_MASK, + MAX77620_CNFG_GPIO_DRV_PUSHPULL); + default: + break; + } + + return -ENOTSUPP; +} + +static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent); + + return regmap_irq_get_virq(chip->gpio_irq_data, offset); +} + +static int max77620_gpio_probe(struct platform_device *pdev) +{ + struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct max77620_gpio *mgpio; + int gpio_irq; + int ret; + + gpio_irq = platform_get_irq(pdev, 0); + if (gpio_irq <= 0) { + dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq); + return -ENODEV; + } + + mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); + if (!mgpio) + return -ENOMEM; + + mgpio->rmap = chip->rmap; + mgpio->dev = &pdev->dev; + mgpio->gpio_irq = gpio_irq; + + mgpio->gpio_chip.label = pdev->name; + mgpio->gpio_chip.parent = &pdev->dev; + mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; + mgpio->gpio_chip.get = max77620_gpio_get; + mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; + mgpio->gpio_chip.set_debounce = max77620_gpio_set_debounce; + mgpio->gpio_chip.set = max77620_gpio_set; + mgpio->gpio_chip.set_single_ended = max77620_gpio_set_single_ended; + mgpio->gpio_chip.to_irq = max77620_gpio_to_irq; + mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; + mgpio->gpio_chip.can_sleep = 1; + mgpio->gpio_chip.base = -1; + mgpio->irq_base = -1; +#ifdef CONFIG_OF_GPIO + mgpio->gpio_chip.of_node = pdev->dev.parent->of_node; +#endif + + platform_set_drvdata(pdev, mgpio); + + ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); + if (ret < 0) { + dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n"); + return ret; + } + + mgpio->gpio_base = mgpio->gpio_chip.base; + ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, mgpio->gpio_irq, + IRQF_ONESHOT, mgpio->irq_base, + &max77620_gpio_irq_chip, + &chip->gpio_irq_data); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret); + return ret; + } + + return 0; +} + +static const struct platform_device_id max77620_gpio_devtype[] = { + { .name = "max77620-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype); + +static struct platform_driver max77620_gpio_driver = { + .driver.name = "max77620-gpio", + .probe = max77620_gpio_probe, + .id_table = max77620_gpio_devtype, +}; + +module_platform_driver(max77620_gpio_driver); + +MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>"); +MODULE_ALIAS("platform:max77620-gpio"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index cc103af..a1210e3 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -187,7 +187,6 @@ MODULE_DEVICE_TABLE(mcb, men_z127_ids); static struct mcb_driver men_z127_driver = { .driver = { .name = "z127-gpio", - .owner = THIS_MODULE, }, .probe = men_z127_probe, .remove = men_z127_remove, diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c new file mode 100644 index 0000000..45b5127 --- /dev/null +++ b/drivers/gpio/gpio-merrifield.c @@ -0,0 +1,444 @@ +/* + * Intel Merrifield SoC GPIO driver + * + * Copyright (c) 2016 Intel Corporation. + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.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. + */ + +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pinctrl/consumer.h> + +#define GCCR 0x000 /* controller configuration */ +#define GPLR 0x004 /* pin level r/o */ +#define GPDR 0x01c /* pin direction */ +#define GPSR 0x034 /* pin set w/o */ +#define GPCR 0x04c /* pin clear w/o */ +#define GRER 0x064 /* rising edge detect */ +#define GFER 0x07c /* falling edge detect */ +#define GFBR 0x094 /* glitch filter bypass */ +#define GIMR 0x0ac /* interrupt mask */ +#define GISR 0x0c4 /* interrupt source */ +#define GITR 0x300 /* input type */ +#define GLPR 0x318 /* level input polarity */ +#define GWMR 0x400 /* wake mask */ +#define GWSR 0x418 /* wake source */ +#define GSIR 0xc00 /* secure input */ + +/* Intel Merrifield has 192 GPIO pins */ +#define MRFLD_NGPIO 192 + +struct mrfld_gpio_pinrange { + unsigned int gpio_base; + unsigned int pin_base; + unsigned int npins; +}; + +#define GPIO_PINRANGE(gstart, gend, pstart) \ + { \ + .gpio_base = (gstart), \ + .pin_base = (pstart), \ + .npins = (gend) - (gstart) + 1, \ + } + +struct mrfld_gpio { + struct gpio_chip chip; + void __iomem *reg_base; + raw_spinlock_t lock; + struct device *dev; +}; + +static const struct mrfld_gpio_pinrange mrfld_gpio_ranges[] = { + GPIO_PINRANGE(0, 11, 146), + GPIO_PINRANGE(12, 13, 144), + GPIO_PINRANGE(14, 15, 35), + GPIO_PINRANGE(16, 16, 164), + GPIO_PINRANGE(17, 18, 105), + GPIO_PINRANGE(19, 22, 101), + GPIO_PINRANGE(23, 30, 107), + GPIO_PINRANGE(32, 43, 67), + GPIO_PINRANGE(44, 63, 195), + GPIO_PINRANGE(64, 67, 140), + GPIO_PINRANGE(68, 69, 165), + GPIO_PINRANGE(70, 71, 65), + GPIO_PINRANGE(72, 76, 228), + GPIO_PINRANGE(77, 86, 37), + GPIO_PINRANGE(87, 87, 48), + GPIO_PINRANGE(88, 88, 47), + GPIO_PINRANGE(89, 96, 49), + GPIO_PINRANGE(97, 97, 34), + GPIO_PINRANGE(102, 119, 83), + GPIO_PINRANGE(120, 123, 79), + GPIO_PINRANGE(124, 135, 115), + GPIO_PINRANGE(137, 142, 158), + GPIO_PINRANGE(154, 163, 24), + GPIO_PINRANGE(164, 176, 215), + GPIO_PINRANGE(177, 189, 127), + GPIO_PINRANGE(190, 191, 178), +}; + +static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned int offset, + unsigned int reg_type_offset) +{ + struct mrfld_gpio *priv = gpiochip_get_data(chip); + u8 reg = offset / 32; + + return priv->reg_base + reg_type_offset + reg * 4; +} + +static int mrfld_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + void __iomem *gplr = gpio_reg(chip, offset, GPLR); + + return !!(readl(gplr) & BIT(offset % 32)); +} + +static void mrfld_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct mrfld_gpio *priv = gpiochip_get_data(chip); + void __iomem *gpsr, *gpcr; + unsigned long flags; + + raw_spin_lock_irqsave(&priv->lock, flags); + + if (value) { + gpsr = gpio_reg(chip, offset, GPSR); + writel(BIT(offset % 32), gpsr); + } else { + gpcr = gpio_reg(chip, offset, GPCR); + writel(BIT(offset % 32), gpcr); + } + + raw_spin_unlock_irqrestore(&priv->lock, flags); +} + +static int mrfld_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct mrfld_gpio *priv = gpiochip_get_data(chip); + void __iomem *gpdr = gpio_reg(chip, offset, GPDR); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&priv->lock, flags); + + value = readl(gpdr); + value &= ~BIT(offset % 32); + writel(value, gpdr); + + raw_spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int mrfld_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct mrfld_gpio *priv = gpiochip_get_data(chip); + void __iomem *gpdr = gpio_reg(chip, offset, GPDR); + unsigned long flags; + + mrfld_gpio_set(chip, offset, value); + + raw_spin_lock_irqsave(&priv->lock, flags); + + value = readl(gpdr); + value |= BIT(offset % 32); + writel(value, gpdr); + + raw_spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void mrfld_irq_ack(struct irq_data *d) +{ + struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d); + u32 gpio = irqd_to_hwirq(d); + void __iomem *gisr = gpio_reg(&priv->chip, gpio, GISR); + unsigned long flags; + + raw_spin_lock_irqsave(&priv->lock, flags); + + writel(BIT(gpio % 32), gisr); + + raw_spin_unlock_irqrestore(&priv->lock, flags); +} + +static void mrfld_irq_unmask_mask(struct irq_data *d, bool unmask) +{ + struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d); + u32 gpio = irqd_to_hwirq(d); + void __iomem *gimr = gpio_reg(&priv->chip, gpio, GIMR); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&priv->lock, flags); + + if (unmask) + value = readl(gimr) | BIT(gpio % 32); + else + value = readl(gimr) & ~BIT(gpio % 32); + writel(value, gimr); + + raw_spin_unlock_irqrestore(&priv->lock, flags); +} + +static void mrfld_irq_mask(struct irq_data *d) +{ + mrfld_irq_unmask_mask(d, false); +} + +static void mrfld_irq_unmask(struct irq_data *d) +{ + mrfld_irq_unmask_mask(d, true); +} + +static int mrfld_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mrfld_gpio *priv = gpiochip_get_data(gc); + u32 gpio = irqd_to_hwirq(d); + void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER); + void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER); + void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR); + void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&priv->lock, flags); + + if (type & IRQ_TYPE_EDGE_RISING) + value = readl(grer) | BIT(gpio % 32); + else + value = readl(grer) & ~BIT(gpio % 32); + writel(value, grer); + + if (type & IRQ_TYPE_EDGE_FALLING) + value = readl(gfer) | BIT(gpio % 32); + else + value = readl(gfer) & ~BIT(gpio % 32); + writel(value, gfer); + + /* + * To prevent glitches from triggering an unintended level interrupt, + * configure GLPR register first and then configure GITR. + */ + if (type & IRQ_TYPE_LEVEL_LOW) + value = readl(glpr) | BIT(gpio % 32); + else + value = readl(glpr) & ~BIT(gpio % 32); + writel(value, glpr); + + if (type & IRQ_TYPE_LEVEL_MASK) { + value = readl(gitr) | BIT(gpio % 32); + writel(value, gitr); + + irq_set_handler_locked(d, handle_level_irq); + } else if (type & IRQ_TYPE_EDGE_BOTH) { + value = readl(gitr) & ~BIT(gpio % 32); + writel(value, gitr); + + irq_set_handler_locked(d, handle_edge_irq); + } + + raw_spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int mrfld_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mrfld_gpio *priv = gpiochip_get_data(gc); + u32 gpio = irqd_to_hwirq(d); + void __iomem *gwmr = gpio_reg(&priv->chip, gpio, GWMR); + void __iomem *gwsr = gpio_reg(&priv->chip, gpio, GWSR); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&priv->lock, flags); + + /* Clear the existing wake status */ + writel(BIT(gpio % 32), gwsr); + + if (on) + value = readl(gwmr) | BIT(gpio % 32); + else + value = readl(gwmr) & ~BIT(gpio % 32); + writel(value, gwmr); + + raw_spin_unlock_irqrestore(&priv->lock, flags); + + dev_dbg(priv->dev, "%sable wake for gpio %u\n", on ? "en" : "dis", gpio); + return 0; +} + +static struct irq_chip mrfld_irqchip = { + .name = "gpio-merrifield", + .irq_ack = mrfld_irq_ack, + .irq_mask = mrfld_irq_mask, + .irq_unmask = mrfld_irq_unmask, + .irq_set_type = mrfld_irq_set_type, + .irq_set_wake = mrfld_irq_set_wake, +}; + +static void mrfld_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct mrfld_gpio *priv = gpiochip_get_data(gc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long base, gpio; + + chained_irq_enter(irqchip, desc); + + /* Check GPIO controller to check which pin triggered the interrupt */ + for (base = 0; base < priv->chip.ngpio; base += 32) { + void __iomem *gisr = gpio_reg(&priv->chip, base, GISR); + void __iomem *gimr = gpio_reg(&priv->chip, base, GIMR); + unsigned long pending, enabled; + + pending = readl(gisr); + enabled = readl(gimr); + + /* Only interrupts that are enabled */ + pending &= enabled; + + for_each_set_bit(gpio, &pending, 32) { + unsigned int irq; + + irq = irq_find_mapping(gc->irqdomain, base + gpio); + generic_handle_irq(irq); + } + } + + chained_irq_exit(irqchip, desc); +} + +static void mrfld_irq_init_hw(struct mrfld_gpio *priv) +{ + void __iomem *reg; + unsigned int base; + + for (base = 0; base < priv->chip.ngpio; base += 32) { + /* Clear the rising-edge detect register */ + reg = gpio_reg(&priv->chip, base, GRER); + writel(0, reg); + /* Clear the falling-edge detect register */ + reg = gpio_reg(&priv->chip, base, GFER); + writel(0, reg); + } +} + +static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + const struct mrfld_gpio_pinrange *range; + struct mrfld_gpio *priv; + u32 gpio_base, irq_base; + void __iomem *base; + unsigned int i; + int retval; + + retval = pcim_enable_device(pdev); + if (retval) + return retval; + + retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev)); + if (retval) { + dev_err(&pdev->dev, "I/O memory mapping error\n"); + return retval; + } + + base = pcim_iomap_table(pdev)[1]; + + irq_base = readl(base); + gpio_base = readl(sizeof(u32) + base); + + /* Release the IO mapping, since we already get the info from BAR1 */ + pcim_iounmap_regions(pdev, BIT(1)); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "can't allocate chip data\n"); + return -ENOMEM; + } + + priv->dev = &pdev->dev; + priv->reg_base = pcim_iomap_table(pdev)[0]; + + priv->chip.label = dev_name(&pdev->dev); + priv->chip.parent = &pdev->dev; + priv->chip.request = gpiochip_generic_request; + priv->chip.free = gpiochip_generic_free; + priv->chip.direction_input = mrfld_gpio_direction_input; + priv->chip.direction_output = mrfld_gpio_direction_output; + priv->chip.get = mrfld_gpio_get; + priv->chip.set = mrfld_gpio_set; + priv->chip.base = gpio_base; + priv->chip.ngpio = MRFLD_NGPIO; + priv->chip.can_sleep = false; + + raw_spin_lock_init(&priv->lock); + + pci_set_drvdata(pdev, priv); + retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); + if (retval) { + dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); + return retval; + } + + for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) { + range = &mrfld_gpio_ranges[i]; + retval = gpiochip_add_pin_range(&priv->chip, + "pinctrl-merrifield", + range->gpio_base, + range->pin_base, + range->npins); + if (retval) { + dev_err(&pdev->dev, "failed to add GPIO pin range\n"); + return retval; + } + } + + retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base, + handle_simple_irq, IRQ_TYPE_NONE); + if (retval) { + dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n"); + return retval; + } + + mrfld_irq_init_hw(priv); + + gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq, + mrfld_irq_handler); + + return 0; +} + +static const struct pci_device_id mrfld_gpio_ids[] = { + { PCI_VDEVICE(INTEL, 0x1199) }, + { } +}; +MODULE_DEVICE_TABLE(pci, mrfld_gpio_ids); + +static struct pci_driver mrfld_gpio_driver = { + .name = "gpio-merrifield", + .id_table = mrfld_gpio_ids, + .probe = mrfld_gpio_probe, +}; + +module_pci_driver(mrfld_gpio_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Merrifield SoC GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 6c1cb3b..6ec144b 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -61,6 +61,8 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include <linux/bitops.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/of_device.h> static void bgpio_write8(void __iomem *reg, unsigned long data) { @@ -569,6 +571,41 @@ static void __iomem *bgpio_map(struct platform_device *pdev, return devm_ioremap_resource(&pdev->dev, r); } +#ifdef CONFIG_OF +static const struct of_device_id bgpio_of_match[] = { + { .compatible = "wd,mbl-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, bgpio_of_match); + +static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, + unsigned long *flags) +{ + struct bgpio_pdata *pdata; + + if (!of_match_device(bgpio_of_match, &pdev->dev)) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(struct bgpio_pdata), + GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->base = -1; + + if (of_property_read_bool(pdev->dev.of_node, "no-output")) + *flags |= BGPIOF_NO_OUTPUT; + + return pdata; +} +#else +static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, + unsigned long *flags) +{ + return NULL; +} +#endif /* CONFIG_OF */ + static int bgpio_pdev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -579,10 +616,19 @@ static int bgpio_pdev_probe(struct platform_device *pdev) void __iomem *dirout; void __iomem *dirin; unsigned long sz; - unsigned long flags = pdev->id_entry->driver_data; + unsigned long flags = 0; int err; struct gpio_chip *gc; - struct bgpio_pdata *pdata = dev_get_platdata(dev); + struct bgpio_pdata *pdata; + + pdata = bgpio_parse_dt(pdev, &flags); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + if (!pdata) { + pdata = dev_get_platdata(dev); + flags = pdev->id_entry->driver_data; + } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (!r) @@ -646,6 +692,7 @@ MODULE_DEVICE_TABLE(platform, bgpio_id_table); static struct platform_driver bgpio_driver = { .driver = { .name = "basic-mmio-gpio", + .of_match_table = of_match_ptr(bgpio_of_match), }, .id_table = bgpio_id_table, .probe = bgpio_pdev_probe, diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index e248707..8394744 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -208,7 +208,6 @@ static int palmas_gpio_probe(struct platform_device *pdev) static struct platform_driver palmas_gpio_driver = { .driver.name = "palmas-gpio", - .driver.owner = THIS_MODULE, .driver.of_match_table = of_palmas_gpio_match, .probe = palmas_gpio_probe, }; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 5e3be32..02f2a56 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -44,7 +44,7 @@ #define PCA_GPIO_MASK 0x00FF #define PCA_INT 0x0100 -#define PCA_PCAL 0x0200 +#define PCA_PCAL 0x0200 #define PCA953X_TYPE 0x1000 #define PCA957X_TYPE 0x2000 #define PCA_TYPE_MASK 0xF000 @@ -67,6 +67,8 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, + { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, + { "max7310", 8 | PCA953X_TYPE, }, { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, @@ -90,7 +92,7 @@ MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids); #define MAX_BANK 5 #define BANK_SZ 8 -#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ) +#define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ) struct pca953x_chip { unsigned gpio_start; @@ -135,7 +137,7 @@ static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val, int off) { - int ret = 0; + int ret; int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); int offset = off / BANK_SZ; @@ -163,10 +165,13 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val) NBANK(chip), val); } else { switch (chip->chip_type) { - case PCA953X_TYPE: - ret = i2c_smbus_write_word_data(chip->client, - reg << 1, cpu_to_le16(get_unaligned((u16 *)val))); + case PCA953X_TYPE: { + __le16 word = cpu_to_le16(get_unaligned((u16 *)val)); + + ret = i2c_smbus_write_word_data(chip->client, reg << 1, + (__force u16)word); break; + } case PCA957X_TYPE: ret = i2c_smbus_write_byte_data(chip->client, reg << 1, val[0]); @@ -235,7 +240,6 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) goto exit; chip->reg_direction[off / BANK_SZ] = reg_val; - ret = 0; exit: mutex_unlock(&chip->i2c_lock); return ret; @@ -286,7 +290,6 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, goto exit; chip->reg_direction[off / BANK_SZ] = reg_val; - ret = 0; exit: mutex_unlock(&chip->i2c_lock); return ret; @@ -351,7 +354,6 @@ exit: mutex_unlock(&chip->i2c_lock); } - static void pca953x_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -820,7 +822,7 @@ static int pca953x_remove(struct i2c_client *client) { struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev); struct pca953x_chip *chip = i2c_get_clientdata(client); - int ret = 0; + int ret; if (pdata && pdata->teardown) { ret = pdata->teardown(client, chip->gpio_chip.base, @@ -861,6 +863,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "maxim,max7315", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,pca6107", .data = OF_953X( 8, PCA_INT), }, + { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), }, { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 169c09a..d168410 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -440,6 +440,14 @@ static int pcf857x_remove(struct i2c_client *client) return status; } +static void pcf857x_shutdown(struct i2c_client *client) +{ + struct pcf857x *gpio = i2c_get_clientdata(client); + + /* Drive all the I/O lines high */ + gpio->write(gpio->client, BIT(gpio->chip.ngpio) - 1); +} + static struct i2c_driver pcf857x_driver = { .driver = { .name = "pcf857x", @@ -447,6 +455,7 @@ static struct i2c_driver pcf857x_driver = { }, .probe = pcf857x_probe, .remove = pcf857x_remove, + .shutdown = pcf857x_shutdown, .id_table = pcf857x_id, }; diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 681c93f..b96e0b4 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -335,6 +335,9 @@ static const struct of_device_id gpio_rcar_of_table[] = { .compatible = "renesas,gpio-r8a7791", .data = &gpio_rcar_info_gen2, }, { + .compatible = "renesas,gpio-r8a7792", + .data = &gpio_rcar_info_gen2, + }, { .compatible = "renesas,gpio-r8a7793", .data = &gpio_rcar_info_gen2, }, { diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index ec945b9..cbf0f9e 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -200,7 +200,6 @@ static int rdc321x_gpio_probe(struct platform_device *pdev) static struct platform_driver rdc321x_gpio_driver = { .driver.name = "rdc321x-gpio", - .driver.owner = THIS_MODULE, .probe = rdc321x_gpio_probe, }; diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index a03b38e..b96990c 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -296,7 +296,6 @@ static int sch311x_gpio_remove(struct platform_device *pdev) static struct platform_driver sch311x_gpio_driver = { .driver.name = DRV_NAME, - .driver.owner = THIS_MODULE, .probe = sch311x_gpio_probe, .remove = sch311x_gpio_remove, }; diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 6f7af28..f675132 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -68,6 +68,22 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) stmpe_reg_write(stmpe, reg, mask); } +static int stmpe_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + int ret; + + ret = stmpe_reg_read(stmpe, reg); + if (ret < 0) + return ret; + + return !(ret & mask); +} + static int stmpe_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int val) { @@ -106,6 +122,7 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) static struct gpio_chip template_chip = { .label = "stmpe", .owner = THIS_MODULE, + .get_direction = stmpe_gpio_get_direction, .direction_input = stmpe_gpio_direction_input, .get = stmpe_gpio_get, .direction_output = stmpe_gpio_direction_output, @@ -416,7 +433,6 @@ static struct platform_driver stmpe_gpio_driver = { .driver = { .suppress_bind_attrs = true, .name = "stmpe-gpio", - .owner = THIS_MODULE, }, .probe = stmpe_gpio_probe, }; diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 24b6d64..537cec7 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -129,7 +129,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val) static const struct syscon_gpio_data clps711x_mctrl_gpio = { /* ARM CLPS711X SYSFLG1 Bits 8-10 */ - .compatible = "cirrus,clps711x-syscon1", + .compatible = "cirrus,ep7209-syscon1", .flags = GPIO_SYSCON_FEAT_IN, .bit_count = 3, .dat_bit_offset = 0x40 * 8 + 8, @@ -168,7 +168,7 @@ static const struct syscon_gpio_data keystone_dsp_gpio = { static const struct of_device_id syscon_gpio_ids[] = { { - .compatible = "cirrus,clps711x-mctrl-gpio", + .compatible = "cirrus,ep7209-mctrl-gpio", .data = &clps711x_mctrl_gpio, }, { diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 2e35ed3..8b36593 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -343,7 +343,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) static struct platform_driver tc3589x_gpio_driver = { .driver.name = "tc3589x-gpio", - .driver.owner = THIS_MODULE, .probe = tc3589x_gpio_probe, }; diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 0eaeac8..1c09a19 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -230,6 +230,12 @@ static const struct of_device_id tps65218_dt_match[] = { }; MODULE_DEVICE_TABLE(of, tps65218_dt_match); +static const struct platform_device_id tps65218_gpio_id_table[] = { + { "tps65218-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); + static struct platform_driver tps65218_gpio_driver = { .driver = { .name = "tps65218-gpio", @@ -237,6 +243,7 @@ static struct platform_driver tps65218_gpio_driver = { }, .probe = tps65218_gpio_probe, .remove = tps65218_gpio_remove, + .id_table = tps65218_gpio_id_table, }; module_platform_driver(tps65218_gpio_driver); diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index 6b15e68..042b9a2 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -131,7 +131,6 @@ static int tps6586x_gpio_probe(struct platform_device *pdev) static struct platform_driver tps6586x_gpio_driver = { .driver.name = "tps6586x-gpio", - .driver.owner = THIS_MODULE, .probe = tps6586x_gpio_probe, }; diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 0ae6a5a..e63d7da 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -184,7 +184,6 @@ skip_init: static struct platform_driver tps65910_gpio_driver = { .driver.name = "tps65910-gpio", - .driver.owner = THIS_MODULE, .probe = tps65910_gpio_probe, }; diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index dec47aa..e6d1328 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -440,7 +440,6 @@ static int vprbrd_gpio_probe(struct platform_device *pdev) static struct platform_driver vprbrd_gpio_driver = { .driver.name = "viperboard-gpio", - .driver.owner = THIS_MODULE, .probe = vprbrd_gpio_probe, }; diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 41ec783..21f97bc 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -296,7 +296,6 @@ static int wm831x_gpio_probe(struct platform_device *pdev) static struct platform_driver wm831x_gpio_driver = { .driver.name = "wm831x-gpio", - .driver.owner = THIS_MODULE, .probe = wm831x_gpio_probe, }; diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index 07d45a3..e976570 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -139,7 +139,6 @@ static int wm8350_gpio_probe(struct platform_device *pdev) static struct platform_driver wm8350_gpio_driver = { .driver.name = "wm8350-gpio", - .driver.owner = THIS_MODULE, .probe = wm8350_gpio_probe, }; diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index 744af38..2457aac 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -299,7 +299,6 @@ static int wm8994_gpio_probe(struct platform_device *pdev) static struct platform_driver wm8994_gpio_driver = { .driver.name = "wm8994-gpio", - .driver.owner = THIS_MODULE, .probe = wm8994_gpio_probe, }; diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index d0fbb7f..14b2a62 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -133,6 +133,53 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) } /** + * xgpio_set_multiple - Write the specified signals of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @mask: Mask of the GPIOS to modify. + * @bits: Value to be wrote on each GPIO + * + * This function writes the specified values into the specified signals of the + * GPIO devices. + */ +static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = gpiochip_get_data(gc); + int index = xgpio_index(chip, 0); + int offset, i; + + spin_lock_irqsave(&chip->gpio_lock[index], flags); + + /* Write to GPIO signals */ + for (i = 0; i < gc->ngpio; i++) { + if (*mask == 0) + break; + if (index != xgpio_index(chip, i)) { + xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET + + xgpio_regoffset(chip, i), + chip->gpio_state[index]); + spin_unlock_irqrestore(&chip->gpio_lock[index], flags); + index = xgpio_index(chip, i); + spin_lock_irqsave(&chip->gpio_lock[index], flags); + } + if (__test_and_clear_bit(i, mask)) { + offset = xgpio_offset(chip, i); + if (test_bit(i, bits)) + chip->gpio_state[index] |= BIT(offset); + else + chip->gpio_state[index] &= ~BIT(offset); + } + } + + xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET + + xgpio_regoffset(chip, i), chip->gpio_state[index]); + + spin_unlock_irqrestore(&chip->gpio_lock[index], flags); +} + +/** * xgpio_dir_in - Set the direction of the specified GPIO signal as input. * @gc: Pointer to gpio_chip device structure. * @gpio: GPIO signal number. @@ -306,6 +353,7 @@ static int xgpio_probe(struct platform_device *pdev) chip->mmchip.gc.direction_output = xgpio_dir_out; chip->mmchip.gc.get = xgpio_get; chip->mmchip.gc.set = xgpio_set; + chip->mmchip.gc.set_multiple = xgpio_set_multiple; chip->mmchip.save_regs = xgpio_save_regs; diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 1a33a19..4620d05 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -19,6 +19,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/irqchip/chained_irq.h> +#include <linux/acpi.h> /* * XLP GPIO has multiple 32 bit registers for each feature where each register @@ -299,7 +300,6 @@ static int xlp_gpio_probe(struct platform_device *pdev) struct gpio_chip *gc; struct resource *iores; struct xlp_gpio_priv *priv; - const struct of_device_id *of_id; void __iomem *gpio_base; int irq_base, irq, err; int ngpio; @@ -321,13 +321,26 @@ static int xlp_gpio_probe(struct platform_device *pdev) if (irq < 0) return irq; - of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev); - if (!of_id) { - dev_err(&pdev->dev, "Failed to get soc type!\n"); - return -ENODEV; - } + if (pdev->dev.of_node) { + const struct of_device_id *of_id; - soc_type = (uintptr_t) of_id->data; + of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "Unable to match OF ID\n"); + return -ENODEV; + } + soc_type = (uintptr_t) of_id->data; + } else { + const struct acpi_device_id *acpi_id; + + acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table, + &pdev->dev); + if (!acpi_id || !acpi_id->driver_data) { + dev_err(&pdev->dev, "Unable to match ACPI ID\n"); + return -ENODEV; + } + soc_type = (uintptr_t) acpi_id->driver_data; + } switch (soc_type) { case XLP_GPIO_VARIANT_XLP832: @@ -388,14 +401,16 @@ static int xlp_gpio_probe(struct platform_device *pdev) gc->get = xlp_gpio_get; spin_lock_init(&priv->lock); - /* XLP has fixed IRQ range for GPIO interrupts */ - if (soc_type == GPIO_VARIANT_VULCAN) - irq_base = irq_alloc_descs(-1, 0, gc->ngpio, 0); - else + + /* XLP(MIPS) has fixed range for GPIO IRQs, Vulcan(ARM64) does not */ + if (soc_type != GPIO_VARIANT_VULCAN) { irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0); - if (irq_base < 0) { - dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n"); - return irq_base; + if (irq_base < 0) { + dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n"); + return irq_base; + } + } else { + irq_base = 0; } err = gpiochip_add_data(gc, priv); @@ -423,10 +438,19 @@ out_free_desc: return err; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xlp_gpio_acpi_match[] = { + { "BRCM9006", GPIO_VARIANT_VULCAN }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match); +#endif + static struct platform_driver xlp_gpio_driver = { .driver = { .name = "xlp-gpio", .of_match_table = xlp_gpio_of_ids, + .acpi_match_table = ACPI_PTR(xlp_gpio_acpi_match), }, .probe = xlp_gpio_probe, }; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 2dc5258..af51461 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -836,6 +836,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip) } acpi_gpiochip_request_regions(acpi_gpio); + acpi_walk_dep_device_list(handle); } void acpi_gpiochip_remove(struct gpio_chip *chip) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 4aabddb..75e7b39 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -27,38 +27,30 @@ #include "gpiolib.h" -/* Private data structure for of_gpiochip_find_and_xlate */ -struct gg_data { - enum of_gpio_flags *flags; - struct of_phandle_args gpiospec; +static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) +{ + return chip->gpiodev->dev.of_node == data; +} - struct gpio_desc *out_gpio; -}; +static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np) +{ + return gpiochip_find(np, of_gpiochip_match_node); +} -/* Private function for resolving node pointer to gpio_chip */ -static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) +static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, + struct of_phandle_args *gpiospec, + enum of_gpio_flags *flags) { - struct gg_data *gg_data = data; int ret; - if ((gc->of_node != gg_data->gpiospec.np) || - (gc->of_gpio_n_cells != gg_data->gpiospec.args_count) || - (!gc->of_xlate)) - return false; - - ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); - if (ret < 0) { - /* We've found a gpio chip, but the translation failed. - * Store translation error in out_gpio. - * Return false to keep looking, as more than one gpio chip - * could be registered per of-node. - */ - gg_data->out_gpio = ERR_PTR(ret); - return false; - } - - gg_data->out_gpio = gpiochip_get_desc(gc, ret); - return true; + if (chip->of_gpio_n_cells != gpiospec->args_count) + return ERR_PTR(-EINVAL); + + ret = chip->of_xlate(chip, gpiospec, flags); + if (ret < 0) + return ERR_PTR(ret); + + return gpiochip_get_desc(chip, ret); } /** @@ -75,34 +67,37 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags) { - /* Return -EPROBE_DEFER to support probe() functions to be called - * later when the GPIO actually becomes available - */ - struct gg_data gg_data = { - .flags = flags, - .out_gpio = ERR_PTR(-EPROBE_DEFER) - }; + struct of_phandle_args gpiospec; + struct gpio_chip *chip; + struct gpio_desc *desc; int ret; - /* .of_xlate might decide to not fill in the flags, so clear it. */ - if (flags) - *flags = 0; - ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, - &gg_data.gpiospec); + &gpiospec); if (ret) { pr_debug("%s: can't parse '%s' property of node '%s[%d]'\n", __func__, propname, np->full_name, index); return ERR_PTR(ret); } - gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); + chip = of_find_gpiochip_by_node(gpiospec.np); + if (!chip) { + desc = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags); + if (IS_ERR(desc)) + goto out; - of_node_put(gg_data.gpiospec.np); pr_debug("%s: parsed '%s' property of node '%s[%d]' - status (%d)\n", __func__, propname, np->full_name, index, - PTR_ERR_OR_ZERO(gg_data.out_gpio)); - return gg_data.out_gpio; + PTR_ERR_OR_ZERO(desc)); + +out: + of_node_put(gpiospec.np); + + return desc; } int of_get_named_gpio_flags(struct device_node *np, const char *list_name, @@ -122,6 +117,7 @@ EXPORT_SYMBOL(of_get_named_gpio_flags); /** * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API * @np: device node to get GPIO from + * @chip: GPIO chip whose hog is parsed * @name: GPIO line name * @lflags: gpio_lookup_flags - returned from of_find_gpio() or * of_parse_own_gpio() @@ -131,19 +127,19 @@ EXPORT_SYMBOL(of_get_named_gpio_flags); * value on the error condition. */ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, + struct gpio_chip *chip, const char **name, enum gpio_lookup_flags *lflags, enum gpiod_flags *dflags) { struct device_node *chip_np; enum of_gpio_flags xlate_flags; - struct gg_data gg_data = { - .flags = &xlate_flags, - }; + struct of_phandle_args gpiospec; + struct gpio_desc *desc; u32 tmp; - int i, ret; + int ret; - chip_np = np->parent; + chip_np = chip->of_node; if (!chip_np) return ERR_PTR(-EINVAL); @@ -155,25 +151,16 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, if (ret) return ERR_PTR(ret); - if (tmp > MAX_PHANDLE_ARGS) - return ERR_PTR(-EINVAL); + gpiospec.np = chip_np; + gpiospec.args_count = tmp; - gg_data.gpiospec.args_count = tmp; - gg_data.gpiospec.np = chip_np; - for (i = 0; i < tmp; i++) { - ret = of_property_read_u32_index(np, "gpios", i, - &gg_data.gpiospec.args[i]); - if (ret) - return ERR_PTR(ret); - } + ret = of_property_read_u32_array(np, "gpios", gpiospec.args, tmp); + if (ret) + return ERR_PTR(ret); - gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); - if (!gg_data.out_gpio) { - if (np->parent == np) - return ERR_PTR(-ENXIO); - else - return ERR_PTR(-EINVAL); - } + desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags); + if (IS_ERR(desc)) + return desc; if (xlate_flags & OF_GPIO_ACTIVE_LOW) *lflags |= GPIO_ACTIVE_LOW; @@ -186,14 +173,14 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, *dflags |= GPIOD_OUT_HIGH; else { pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n", - desc_to_gpio(gg_data.out_gpio), np->name); + desc_to_gpio(desc), np->name); return ERR_PTR(-EINVAL); } if (name && of_property_read_string(np, "line-name", name)) *name = np->name; - return gg_data.out_gpio; + return desc; } /** @@ -262,7 +249,7 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) if (!of_property_read_bool(np, "gpio-hog")) continue; - desc = of_parse_own_gpio(np, &name, &lflags, &dflags); + desc = of_parse_own_gpio(np, chip, &name, &lflags, &dflags); if (IS_ERR(desc)) continue; @@ -410,6 +397,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) break; pctldev = of_pinctrl_get(pinspec.np); + of_node_put(pinspec.np); if (!pctldev) return -EPROBE_DEFER; @@ -487,6 +475,9 @@ int of_gpiochip_add(struct gpio_chip *chip) chip->of_xlate = of_gpio_simple_xlate; } + if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) + return -EINVAL; + status = of_gpiochip_add_pin_range(chip); if (status) return status; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index be74bd3..53ff25a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -16,11 +16,14 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> #include <linux/pinctrl/consumer.h> -#include <linux/idr.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/compat.h> +#include <linux/anon_inodes.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/timekeeping.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" @@ -310,6 +313,497 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) return 0; } +/* + * GPIO line handle management + */ + +/** + * struct linehandle_state - contains the state of a userspace handle + * @gdev: the GPIO device the handle pertains to + * @label: consumer label used to tag descriptors + * @descs: the GPIO descriptors held by this handle + * @numdescs: the number of descriptors held in the descs array + */ +struct linehandle_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *descs[GPIOHANDLES_MAX]; + u32 numdescs; +}; + +static long linehandle_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct linehandle_state *lh = filep->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + int i; + + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + int val; + + /* TODO: check if descriptors are really input */ + for (i = 0; i < lh->numdescs; i++) { + val = gpiod_get_value_cansleep(lh->descs[i]); + if (val < 0) + return val; + ghd.values[i] = val; + } + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { + int vals[GPIOHANDLES_MAX]; + + /* TODO: check if descriptors are really output */ + if (copy_from_user(&ghd, ip, sizeof(ghd))) + return -EFAULT; + + /* Clamp all values to [0,1] */ + for (i = 0; i < lh->numdescs; i++) + vals[i] = !!ghd.values[i]; + + /* Reuse the array setting function */ + gpiod_set_array_value_complex(false, + true, + lh->numdescs, + lh->descs, + vals); + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int linehandle_release(struct inode *inode, struct file *filep) +{ + struct linehandle_state *lh = filep->private_data; + struct gpio_device *gdev = lh->gdev; + int i; + + for (i = 0; i < lh->numdescs; i++) + gpiod_free(lh->descs[i]); + kfree(lh->label); + kfree(lh); + put_device(&gdev->dev); + return 0; +} + +static const struct file_operations linehandle_fileops = { + .release = linehandle_release, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = linehandle_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = linehandle_ioctl_compat, +#endif +}; + +static int linehandle_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpiohandle_request handlereq; + struct linehandle_state *lh; + int fd, i, ret; + + if (copy_from_user(&handlereq, ip, sizeof(handlereq))) + return -EFAULT; + if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) + return -EINVAL; + + lh = kzalloc(sizeof(*lh), GFP_KERNEL); + if (!lh) + return -ENOMEM; + lh->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; + if (strlen(handlereq.consumer_label)) { + lh->label = kstrdup(handlereq.consumer_label, + GFP_KERNEL); + if (!lh->label) { + ret = -ENOMEM; + goto out_free_lh; + } + } + + /* Request each GPIO */ + for (i = 0; i < handlereq.lines; i++) { + u32 offset = handlereq.lineoffsets[i]; + u32 lflags = handlereq.flags; + struct gpio_desc *desc; + + desc = &gdev->descs[offset]; + ret = gpiod_request(desc, lh->label); + if (ret) + goto out_free_descs; + lh->descs[i] = desc; + + if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!handlereq.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + goto out_free_descs; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_descs; + } + dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", + offset); + } + /* Let i point at the last handle */ + i--; + lh->numdescs = handlereq.lines; + + fd = anon_inode_getfd("gpio-linehandle", + &linehandle_fileops, + lh, + O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_descs; + } + + handlereq.fd = fd; + if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { + ret = -EFAULT; + goto out_free_descs; + } + + dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", + lh->numdescs); + + return 0; + +out_free_descs: + for (; i >= 0; i--) + gpiod_free(lh->descs[i]); + kfree(lh->label); +out_free_lh: + kfree(lh); + put_device(&gdev->dev); + return ret; +} + +/* + * GPIO line event management + */ + +/** + * struct lineevent_state - contains the state of a userspace event + * @gdev: the GPIO device the event pertains to + * @label: consumer label used to tag descriptors + * @desc: the GPIO descriptor held by this event + * @eflags: the event flags this line was requested with + * @irq: the interrupt that trigger in response to events on this GPIO + * @wait: wait queue that handles blocking reads of events + * @events: KFIFO for the GPIO events + * @read_lock: mutex lock to protect reads from colliding with adding + * new events to the FIFO + */ +struct lineevent_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *desc; + u32 eflags; + int irq; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioevent_data, 16); + struct mutex read_lock; +}; + +static unsigned int lineevent_poll(struct file *filep, + struct poll_table_struct *wait) +{ + struct lineevent_state *le = filep->private_data; + unsigned int events = 0; + + poll_wait(filep, &le->wait, wait); + + if (!kfifo_is_empty(&le->events)) + events = POLLIN | POLLRDNORM; + + return events; +} + + +static ssize_t lineevent_read(struct file *filep, + char __user *buf, + size_t count, + loff_t *f_ps) +{ + struct lineevent_state *le = filep->private_data; + unsigned int copied; + int ret; + + if (count < sizeof(struct gpioevent_data)) + return -EINVAL; + + do { + if (kfifo_is_empty(&le->events)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(le->wait, + !kfifo_is_empty(&le->events)); + if (ret) + return ret; + } + + if (mutex_lock_interruptible(&le->read_lock)) + return -ERESTARTSYS; + ret = kfifo_to_user(&le->events, buf, count, &copied); + mutex_unlock(&le->read_lock); + + if (ret) + return ret; + + /* + * If we couldn't read anything from the fifo (a different + * thread might have been faster) we either return -EAGAIN if + * the file descriptor is non-blocking, otherwise we go back to + * sleep and wait for more data to arrive. + */ + if (copied == 0 && (filep->f_flags & O_NONBLOCK)) + return -EAGAIN; + + } while (copied == 0); + + return copied; +} + +static int lineevent_release(struct inode *inode, struct file *filep) +{ + struct lineevent_state *le = filep->private_data; + struct gpio_device *gdev = le->gdev; + + free_irq(le->irq, le); + gpiod_free(le->desc); + kfree(le->label); + kfree(le); + put_device(&gdev->dev); + return 0; +} + +static long lineevent_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct lineevent_state *le = filep->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + + /* + * We can get the value for an event line but not set it, + * because it is input by definition. + */ + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + int val; + + val = gpiod_get_value_cansleep(le->desc); + if (val < 0) + return val; + ghd.values[0] = val; + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations lineevent_fileops = { + .release = lineevent_release, + .read = lineevent_read, + .poll = lineevent_poll, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = lineevent_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lineevent_ioctl_compat, +#endif +}; + +static irqreturn_t lineevent_irq_thread(int irq, void *p) +{ + struct lineevent_state *le = p; + struct gpioevent_data ge; + int ret; + + ge.timestamp = ktime_get_real_ns(); + + if (le->eflags & GPIOEVENT_REQUEST_BOTH_EDGES) { + int level = gpiod_get_value_cansleep(le->desc); + + if (level) + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + else + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else { + return IRQ_NONE; + } + + ret = kfifo_put(&le->events, ge); + if (ret != 0) + wake_up_poll(&le->wait, POLLIN); + + return IRQ_HANDLED; +} + +static int lineevent_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpioevent_request eventreq; + struct lineevent_state *le; + struct gpio_desc *desc; + u32 offset; + u32 lflags; + u32 eflags; + int fd; + int ret; + int irqflags = 0; + + if (copy_from_user(&eventreq, ip, sizeof(eventreq))) + return -EFAULT; + + le = kzalloc(sizeof(*le), GFP_KERNEL); + if (!le) + return -ENOMEM; + le->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; + if (strlen(eventreq.consumer_label)) { + le->label = kstrdup(eventreq.consumer_label, + GFP_KERNEL); + if (!le->label) { + ret = -ENOMEM; + goto out_free_le; + } + } + + offset = eventreq.lineoffset; + lflags = eventreq.handleflags; + eflags = eventreq.eventflags; + + /* This is just wrong: we don't look for events on output lines */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + ret = -EINVAL; + goto out_free_label; + } + + desc = &gdev->descs[offset]; + ret = gpiod_request(desc, le->label); + if (ret) + goto out_free_desc; + le->desc = desc; + le->eflags = eflags; + + if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_desc; + + le->irq = gpiod_to_irq(desc); + if (le->irq <= 0) { + ret = -ENODEV; + goto out_free_desc; + } + + if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) + irqflags |= IRQF_TRIGGER_RISING; + if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) + irqflags |= IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + irqflags |= IRQF_SHARED; + + INIT_KFIFO(le->events); + init_waitqueue_head(&le->wait); + mutex_init(&le->read_lock); + + /* Request a thread to read the events */ + ret = request_threaded_irq(le->irq, + NULL, + lineevent_irq_thread, + irqflags, + le->label, + le); + if (ret) + goto out_free_desc; + + fd = anon_inode_getfd("gpio-event", + &lineevent_fileops, + le, + O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_irq; + } + + eventreq.fd = fd; + if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { + ret = -EFAULT; + goto out_free_irq; + } + + return 0; + +out_free_irq: + free_irq(le->irq, le); +out_free_desc: + gpiod_free(le->desc); +out_free_label: + kfree(le->label); +out_free_le: + kfree(le); + put_device(&gdev->dev); + return ret; +} + /** * gpio_ioctl() - ioctl handler for the GPIO chardev */ @@ -385,6 +879,10 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; return 0; + } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { + return linehandle_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { + return lineevent_create(gdev, ip); } return -EINVAL; } @@ -548,13 +1046,14 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) if (chip->parent) { gdev->dev.parent = chip->parent; gdev->dev.of_node = chip->parent->of_node; - } else { + } + #ifdef CONFIG_OF_GPIO /* If the gpiochip has an assigned OF node this takes precedence */ - if (chip->of_node) - gdev->dev.of_node = chip->of_node; + if (chip->of_node) + gdev->dev.of_node = chip->of_node; #endif - } + gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL); if (gdev->id < 0) { status = gdev->id; @@ -2333,7 +2832,7 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, &of_flags); - if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER)) + if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) break; } |