diff options
Diffstat (limited to 'drivers/gpio')
27 files changed, 1616 insertions, 1496 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 23ca51ee6b28..f235eae04c16 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -242,6 +242,17 @@ config GPIO_ICH If unsure, say N. +config GPIO_INGENIC + tristate "Ingenic JZ47xx SoCs GPIO support" + depends on OF + depends on MACH_INGENIC || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + Say yes here to support the GPIO functionality present on the + JZ4740 and JZ4780 SoCs from Ingenic. + + If unsure, say N. + config GPIO_IOP tristate "Intel IOP GPIO" depends on ARCH_IOP32X || ARCH_IOP33X || COMPILE_TEST @@ -326,9 +337,10 @@ config GPIO_MPC8XXX config GPIO_MVEBU def_bool y - depends on PLAT_ORION + depends on PLAT_ORION || ARCH_MVEBU depends on OF_GPIO select GENERIC_IRQ_CHIP + select REGMAP_MMIO config GPIO_MXC def_bool y @@ -504,12 +516,13 @@ config GPIO_XILINX config GPIO_XLP tristate "Netlogic XLP GPIO support" - depends on OF_GPIO && (CPU_XLP || ARCH_VULCAN || ARCH_THUNDER2 || COMPILE_TEST) + depends on OF_GPIO && (CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST) select GPIOLIB_IRQCHIP help This driver provides support for GPIO interface on Netlogic XLP MIPS64 SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, - XLP9XX and XLP5XX. + XLP9XX and XLP5XX. The same GPIO controller block is also present in + Cavium's ThunderX2 CN99XX SoCs. If unsure, say N. @@ -952,6 +965,16 @@ config GPIO_LP873X This driver can also be built as a module. If so, the module will be called gpio-lp873x. +config GPIO_LP87565 + tristate "TI LP87565 GPIO" + depends on MFD_TI_LP87565 + help + This driver supports the GPIO on TI Lp873565 PMICs. 3 GPIOs are present + on LP87565 PMICs. + + This driver can also be built as a module. If so, the module will be + called gpio-lp87565. + config GPIO_MAX77620 tristate "GPIO support for PMIC MAX77620 and MAX20024" depends on MFD_MAX77620 @@ -1225,22 +1248,11 @@ config GPIO_PISOSR GPIO driver for SPI compatible parallel-in/serial-out shift registers. These are input only devices. -endmenu - -menu "SPI or I2C GPIO expanders" - depends on (SPI_MASTER && !I2C) || I2C - -config GPIO_MCP23S08 - tristate "Microchip MCP23xxx I/O expander" - depends on OF_GPIO - select GPIOLIB_IRQCHIP - select REGMAP_I2C if I2C - select REGMAP if SPI_MASTER +config GPIO_XRA1403 + tristate "EXAR XRA1403 16-bit GPIO expander" + select REGMAP_SPI help - SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 - I/O expanders. - This provides a GPIO interface supporting inputs and outputs. - The I2C versions of the chips can be used as interrupt-controller. + GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander. endmenu diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 68b96277d9fa..a9fda6c55113 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o +obj-$(CONFIG_GPIO_INGENIC) += gpio-ingenic.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o @@ -66,6 +67,7 @@ obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o +obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o @@ -77,7 +79,6 @@ 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 obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o @@ -141,6 +142,7 @@ obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o +obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index c0f718b12317..e717f8dc3966 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -16,7 +16,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/i2c/adp5588.h> +#include <linux/platform_data/adp5588.h> #define DRV_NAME "adp5588-gpio" diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index cd23fd727f95..d4e6ba0301bc 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -33,9 +33,23 @@ static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; + bool persistent = gpiochip_line_is_persistent(chip, offset); + bool change; + int ret; - return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, - ARIZONA_GPN_DIR, ARIZONA_GPN_DIR); + ret = regmap_update_bits_check(arizona->regmap, + ARIZONA_GPIO1_CTRL + offset, + ARIZONA_GPN_DIR, ARIZONA_GPN_DIR, + &change); + if (ret < 0) + return ret; + + if (change && persistent) { + pm_runtime_mark_last_busy(chip->parent); + pm_runtime_put_autosuspend(chip->parent); + } + + return 0; } static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -85,6 +99,21 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip, { struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; + bool persistent = gpiochip_line_is_persistent(chip, offset); + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val); + if (ret < 0) + return ret; + + if ((val & ARIZONA_GPN_DIR) && persistent) { + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) { + dev_err(chip->parent, "Failed to resume: %d\n", ret); + return ret; + } + } if (value) value = ARIZONA_GPN_LVL; @@ -158,6 +187,8 @@ static int arizona_gpio_probe(struct platform_device *pdev) else arizona_gpio->gpio_chip.base = -1; + pm_runtime_enable(&pdev->dev); + ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip, arizona_gpio); if (ret < 0) { diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index ac173575d3f6..65cb359308e3 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -437,6 +437,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) { unsigned gpio, bank; int irq; + int ret; struct clk *clk; u32 binten = 0; unsigned ngpio, bank_irq; @@ -480,12 +481,15 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) PTR_ERR(clk)); return PTR_ERR(clk); } - clk_prepare_enable(clk); + ret = clk_prepare_enable(clk); + if (ret) + return ret; if (!pdata->gpio_unbanked) { irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0); if (irq < 0) { dev_err(dev, "Couldn't allocate IRQ numbers\n"); + clk_disable_unprepare(clk); return irq; } @@ -494,6 +498,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) chips); if (!irq_domain) { dev_err(dev, "Couldn't register an IRQ domain\n"); + clk_disable_unprepare(clk); return -ENODEV; } } @@ -562,8 +567,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) sizeof(struct davinci_gpio_irq_data), GFP_KERNEL); - if (!irqdata) + if (!irqdata) { + clk_disable_unprepare(clk); return -ENOMEM; + } irqdata->regs = g; irqdata->bank_num = bank; diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index f051c4552af5..c07ada9c7af6 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -288,7 +288,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) irq_setup_alt_chip(d, type); dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level); - dwapb_write(gpio, GPIO_INT_POLARITY, polarity); + if (type != IRQ_TYPE_EDGE_BOTH) + dwapb_write(gpio, GPIO_INT_POLARITY, polarity); spin_unlock_irqrestore(&gc->bgpio_lock, flags); return 0; diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 081076771217..fb8d304cfa17 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -31,6 +31,7 @@ struct exar_gpio_chip { int index; void __iomem *regs; char name[20]; + unsigned int first_pin; }; static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, @@ -51,11 +52,12 @@ static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, static int exar_set_direction(struct gpio_chip *chip, int direction, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - exar_update(chip, addr, direction, offset % 8); + exar_update(chip, addr, direction, bit); return 0; } @@ -68,41 +70,38 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg) value = readb(exar_gpio->regs + reg); mutex_unlock(&exar_gpio->lock); - return !!value; + return value; } static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; - int val; - - addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - val = exar_get(chip, addr) >> (offset % 8); + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - return !!val; + return !!(exar_get(chip, addr) & BIT(bit)); } static int exar_get_value(struct gpio_chip *chip, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; - int val; - - addr = bank ? EXAR_OFFSET_MPIOLVL_LO : EXAR_OFFSET_MPIOLVL_HI; - val = exar_get(chip, addr) >> (offset % 8); + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - return !!val; + return !!(exar_get(chip, addr) & BIT(bit)); } static void exar_set_value(struct gpio_chip *chip, unsigned int offset, int value) { - unsigned int bank = offset / 8; - unsigned int addr; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - exar_update(chip, addr, value, offset % 8); + exar_update(chip, addr, value, bit); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, @@ -119,27 +118,30 @@ static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) static int gpio_exar_probe(struct platform_device *pdev) { - struct pci_dev *pcidev = platform_get_drvdata(pdev); + struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent); struct exar_gpio_chip *exar_gpio; + u32 first_pin, ngpios; void __iomem *p; int index, ret; - if (pcidev->vendor != PCI_VENDOR_ID_EXAR) - return -ENODEV; - /* - * Map the pci device to get the register addresses. - * We will need to read and write those registers to control - * the GPIO pins. - * Using managed functions will save us from unmaping on exit. - * As the device is enabled using managed functions by the - * UART driver we can also use managed functions here. + * The UART driver must have mapped region 0 prior to registering this + * device - use it. */ - p = pcim_iomap(pcidev, 0, 0); + p = pcim_iomap_table(pcidev)[0]; if (!p) return -ENOMEM; - exar_gpio = devm_kzalloc(&pcidev->dev, sizeof(*exar_gpio), GFP_KERNEL); + ret = device_property_read_u32(&pdev->dev, "linux,first-pin", + &first_pin); + if (ret) + return ret; + + ret = device_property_read_u32(&pdev->dev, "ngpios", &ngpios); + if (ret) + return ret; + + exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL); if (!exar_gpio) return -ENOMEM; @@ -149,18 +151,19 @@ static int gpio_exar_probe(struct platform_device *pdev) sprintf(exar_gpio->name, "exar_gpio%d", index); exar_gpio->gpio_chip.label = exar_gpio->name; - exar_gpio->gpio_chip.parent = &pcidev->dev; + exar_gpio->gpio_chip.parent = &pdev->dev; exar_gpio->gpio_chip.direction_output = exar_direction_output; exar_gpio->gpio_chip.direction_input = exar_direction_input; exar_gpio->gpio_chip.get_direction = exar_get_direction; exar_gpio->gpio_chip.get = exar_get_value; exar_gpio->gpio_chip.set = exar_set_value; exar_gpio->gpio_chip.base = -1; - exar_gpio->gpio_chip.ngpio = 16; + exar_gpio->gpio_chip.ngpio = ngpios; exar_gpio->regs = p; exar_gpio->index = index; + exar_gpio->first_pin = first_pin; - ret = devm_gpiochip_add_data(&pcidev->dev, + ret = devm_gpiochip_add_data(&pdev->dev, &exar_gpio->gpio_chip, exar_gpio); if (ret) goto err_destroy; diff --git a/drivers/gpio/gpio-ingenic.c b/drivers/gpio/gpio-ingenic.c new file mode 100644 index 000000000000..254780730b95 --- /dev/null +++ b/drivers/gpio/gpio-ingenic.c @@ -0,0 +1,394 @@ +/* + * Ingenic JZ47xx GPIO driver + * + * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/consumer.h> +#include <linux/regmap.h> + +#define GPIO_PIN 0x00 +#define GPIO_MSK 0x20 + +#define JZ4740_GPIO_DATA 0x10 +#define JZ4740_GPIO_SELECT 0x50 +#define JZ4740_GPIO_DIR 0x60 +#define JZ4740_GPIO_TRIG 0x70 +#define JZ4740_GPIO_FLAG 0x80 + +#define JZ4770_GPIO_INT 0x10 +#define JZ4770_GPIO_PAT1 0x30 +#define JZ4770_GPIO_PAT0 0x40 +#define JZ4770_GPIO_FLAG 0x50 + +#define REG_SET(x) ((x) + 0x4) +#define REG_CLEAR(x) ((x) + 0x8) + +enum jz_version { + ID_JZ4740, + ID_JZ4770, + ID_JZ4780, +}; + +struct ingenic_gpio_chip { + struct regmap *map; + struct gpio_chip gc; + struct irq_chip irq_chip; + unsigned int irq, reg_base; + enum jz_version version; +}; + +static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) +{ + unsigned int val; + + regmap_read(jzgc->map, jzgc->reg_base + reg, &val); + + return (u32) val; +} + +static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, + u8 reg, u8 offset, bool set) +{ + if (set) + reg = REG_SET(reg); + else + reg = REG_CLEAR(reg); + + regmap_write(jzgc->map, jzgc->reg_base + reg, BIT(offset)); +} + +static inline bool gpio_get_value(struct ingenic_gpio_chip *jzgc, u8 offset) +{ + unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN); + + return !!(val & BIT(offset)); +} + +static void gpio_set_value(struct ingenic_gpio_chip *jzgc, u8 offset, int value) +{ + if (jzgc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); +} + +static void irq_set_type(struct ingenic_gpio_chip *jzgc, + u8 offset, unsigned int type) +{ + u8 reg1, reg2; + + if (jzgc->version >= ID_JZ4770) { + reg1 = JZ4770_GPIO_PAT1; + reg2 = JZ4770_GPIO_PAT0; + } else { + reg1 = JZ4740_GPIO_TRIG; + reg2 = JZ4740_GPIO_DIR; + } + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + gpio_ingenic_set_bit(jzgc, reg2, offset, true); + gpio_ingenic_set_bit(jzgc, reg1, offset, true); + break; + case IRQ_TYPE_EDGE_FALLING: + gpio_ingenic_set_bit(jzgc, reg2, offset, false); + gpio_ingenic_set_bit(jzgc, reg1, offset, true); + break; + case IRQ_TYPE_LEVEL_HIGH: + gpio_ingenic_set_bit(jzgc, reg2, offset, true); + gpio_ingenic_set_bit(jzgc, reg1, offset, false); + break; + case IRQ_TYPE_LEVEL_LOW: + default: + gpio_ingenic_set_bit(jzgc, reg2, offset, false); + gpio_ingenic_set_bit(jzgc, reg1, offset, false); + break; + } +} + +static void ingenic_gpio_irq_mask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); +} + +static void ingenic_gpio_irq_unmask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); +} + +static void ingenic_gpio_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + int irq = irqd->hwirq; + + if (jzgc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); + + ingenic_gpio_irq_unmask(irqd); +} + +static void ingenic_gpio_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + int irq = irqd->hwirq; + + ingenic_gpio_irq_mask(irqd); + + if (jzgc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); +} + +static void ingenic_gpio_irq_ack(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + int irq = irqd->hwirq; + bool high; + + if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) { + /* + * Switch to an interrupt for the opposite edge to the one that + * triggered the interrupt being ACKed. + */ + high = gpio_get_value(jzgc, irq); + if (high) + irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING); + else + irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING); + } + + if (jzgc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); +} + +static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + irq_set_handler_locked(irqd, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: + irq_set_handler_locked(irqd, handle_level_irq); + break; + default: + irq_set_handler_locked(irqd, handle_bad_irq); + } + + if (type == IRQ_TYPE_EDGE_BOTH) { + /* + * The hardware does not support interrupts on both edges. The + * best we can do is to set up a single-edge interrupt and then + * switch to the opposing edge when ACKing the interrupt. + */ + bool high = gpio_get_value(jzgc, irqd->hwirq); + + type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING; + } + + irq_set_type(jzgc, irqd->hwirq, type); + return 0; +} + +static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + return irq_set_irq_wake(jzgc->irq, on); +} + +static void ingenic_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data); + unsigned long flag, i; + + chained_irq_enter(irq_chip, desc); + + if (jzgc->version >= ID_JZ4770) + flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG); + else + flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG); + + for_each_set_bit(i, &flag, 32) + generic_handle_irq(irq_linear_revmap(gc->irqdomain, i)); + chained_irq_exit(irq_chip, desc); +} + +static void ingenic_gpio_set(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + gpio_set_value(jzgc, offset, value); +} + +static int ingenic_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + return (int) gpio_get_value(jzgc, offset); +} + +static int ingenic_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + return pinctrl_gpio_direction_input(gc->base + offset); +} + +static int ingenic_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + ingenic_gpio_set(gc, offset, value); + return pinctrl_gpio_direction_output(gc->base + offset); +} + +static const struct of_device_id ingenic_gpio_of_match[] = { + { .compatible = "ingenic,jz4740-gpio", .data = (void *)ID_JZ4740 }, + { .compatible = "ingenic,jz4770-gpio", .data = (void *)ID_JZ4770 }, + { .compatible = "ingenic,jz4780-gpio", .data = (void *)ID_JZ4780 }, + {}, +}; +MODULE_DEVICE_TABLE(of, ingenic_gpio_of_match); + +static int ingenic_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id = of_match_device( + ingenic_gpio_of_match, dev); + struct ingenic_gpio_chip *jzgc; + u32 bank; + int err; + + jzgc = devm_kzalloc(dev, sizeof(*jzgc), GFP_KERNEL); + if (!jzgc) + return -ENOMEM; + + jzgc->map = dev_get_drvdata(dev->parent); + if (!jzgc->map) { + dev_err(dev, "Cannot get parent regmap\n"); + return -ENXIO; + } + + err = of_property_read_u32(dev->of_node, "reg", &bank); + if (err) { + dev_err(dev, "Cannot read \"reg\" property: %i\n", err); + return err; + } + + jzgc->reg_base = bank * 0x100; + + jzgc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "GPIO%c", 'A' + bank); + if (!jzgc->gc.label) + return -ENOMEM; + + /* DO NOT EXPAND THIS: FOR BACKWARD GPIO NUMBERSPACE COMPATIBIBILITY + * ONLY: WORK TO TRANSITION CONSUMERS TO USE THE GPIO DESCRIPTOR API IN + * <linux/gpio/consumer.h> INSTEAD. + */ + jzgc->gc.base = bank * 32; + + jzgc->gc.ngpio = 32; + jzgc->gc.parent = dev; + jzgc->gc.of_node = dev->of_node; + jzgc->gc.owner = THIS_MODULE; + jzgc->version = (enum jz_version)of_id->data; + + jzgc->gc.set = ingenic_gpio_set; + jzgc->gc.get = ingenic_gpio_get; + jzgc->gc.direction_input = ingenic_gpio_direction_input; + jzgc->gc.direction_output = ingenic_gpio_direction_output; + + if (of_property_read_bool(dev->of_node, "gpio-ranges")) { + jzgc->gc.request = gpiochip_generic_request; + jzgc->gc.free = gpiochip_generic_free; + } + + err = devm_gpiochip_add_data(dev, &jzgc->gc, jzgc); + if (err) + return err; + + jzgc->irq = irq_of_parse_and_map(dev->of_node, 0); + if (!jzgc->irq) + return -EINVAL; + + jzgc->irq_chip.name = jzgc->gc.label; + jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable; + jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable; + jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask; + jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask; + jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack; + jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type; + jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake; + jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; + + err = gpiochip_irqchip_add(&jzgc->gc, &jzgc->irq_chip, 0, + handle_level_irq, IRQ_TYPE_NONE); + if (err) + return err; + + gpiochip_set_chained_irqchip(&jzgc->gc, &jzgc->irq_chip, + jzgc->irq, ingenic_gpio_irq_handler); + return 0; +} + +static int ingenic_gpio_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver ingenic_gpio_driver = { + .driver = { + .name = "gpio-ingenic", + .of_match_table = of_match_ptr(ingenic_gpio_of_match), + }, + .probe = ingenic_gpio_probe, + .remove = ingenic_gpio_remove, +}; + +static int __init ingenic_gpio_drv_register(void) +{ + return platform_driver_register(&ingenic_gpio_driver); +} +subsys_initcall(ingenic_gpio_drv_register); + +static void __exit ingenic_gpio_drv_unregister(void) +{ + platform_driver_unregister(&ingenic_gpio_driver); +} +module_exit(ingenic_gpio_drv_unregister); + +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_DESCRIPTION("Ingenic JZ47xx GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c new file mode 100644 index 000000000000..6313c50bb91b --- /dev/null +++ b/drivers/gpio/gpio-lp87565.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Keerthy <j-keerthy@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 LP873X driver + */ + +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <linux/mfd/lp87565.h> + +struct lp87565_gpio { + struct gpio_chip chip; + struct regmap *map; +}; + +static int lp87565_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->map, LP87565_REG_GPIO_CONFIG, &val); + if (ret < 0) + return ret; + + return !(val & BIT(offset)); +} + +static int lp87565_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset), 0); +} + +static int lp87565_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset), !value ? BIT(offset) : 0); +} + +static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->map, LP87565_REG_GPIO_IN, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(offset)); +} + +static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, + BIT(offset), value ? BIT(offset) : 0); +} + +static int lp87565_gpio_request(struct gpio_chip *gc, unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(gc); + int ret; + + switch (offset) { + case 0: + case 1: + case 2: + /* + * MUX can program the pin to be in EN1/2/3 pin mode + * Or GPIO1/2/3 mode. + * Setup the GPIO*_SEL MUX to GPIO mode + */ + ret = regmap_update_bits(gpio->map, + LP87565_REG_PIN_FUNCTION, + BIT(offset), BIT(offset)); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int lp87565_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset + + __ffs(LP87565_GOIO1_OD)), + BIT(offset + + __ffs(LP87565_GOIO1_OD))); + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset + + __ffs(LP87565_GOIO1_OD)), 0); + default: + return -ENOTSUPP; + } +} + +static const struct gpio_chip template_chip = { + .label = "lp87565-gpio", + .owner = THIS_MODULE, + .request = lp87565_gpio_request, + .get_direction = lp87565_gpio_get_direction, + .direction_input = lp87565_gpio_direction_input, + .direction_output = lp87565_gpio_direction_output, + .get = lp87565_gpio_get, + .set = lp87565_gpio_set, + .set_config = lp87565_gpio_set_config, + .base = -1, + .ngpio = 3, + .can_sleep = true, +}; + +static int lp87565_gpio_probe(struct platform_device *pdev) +{ + struct lp87565_gpio *gpio; + struct lp87565 *lp87565; + int ret; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + lp87565 = dev_get_drvdata(pdev->dev.parent); + gpio->chip = template_chip; + gpio->chip.parent = lp87565->dev; + gpio->map = lp87565->regmap; + + ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + return 0; +} + +static const struct platform_device_id lp87565_gpio_id_table[] = { + { "lp87565-q1-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table); + +static struct platform_driver lp87565_gpio_driver = { + .driver = { + .name = "lp87565-gpio", + }, + .probe = lp87565_gpio_probe, + .id_table = lp87565_gpio_id_table, +}; +module_platform_driver(lp87565_gpio_driver); + +MODULE_AUTHOR("Keerthy <j-keerthy@ti.com>"); +MODULE_DESCRIPTION("LP87565 GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 4ea4c6a1313b..7f4d26ce5f23 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -20,7 +20,7 @@ #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/i2c/max732x.h> +#include <linux/platform_data/max732x.h> #include <linux/of.h> diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c deleted file mode 100644 index 2a57d024481d..000000000000 --- a/drivers/gpio/gpio-mcp23s08.c +++ /dev/null @@ -1,1047 +0,0 @@ -/* - * MCP23S08 SPI/I2C GPIO gpio expander driver - * - * The inputs and outputs of the mcp23s08, mcp23s17, mcp23008 and mcp23017 are - * supported. - * For the I2C versions of the chips (mcp23008 and mcp23017) generation of - * interrupts is also supported. - * The hardware of the SPI versions of the chips (mcp23s08 and mcp23s17) is - * also capable of generating interrupts, but the linux driver does not - * support that yet. - */ - -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/spi/spi.h> -#include <linux/spi/mcp23s08.h> -#include <linux/slab.h> -#include <asm/byteorder.h> -#include <linux/interrupt.h> -#include <linux/of_irq.h> -#include <linux/of_device.h> -#include <linux/regmap.h> - -/** - * MCP types supported by driver - */ -#define MCP_TYPE_S08 0 -#define MCP_TYPE_S17 1 -#define MCP_TYPE_008 2 -#define MCP_TYPE_017 3 -#define MCP_TYPE_S18 4 - -/* Registers are all 8 bits wide. - * - * The mcp23s17 has twice as many bits, and can be configured to work - * with either 16 bit registers or with two adjacent 8 bit banks. - */ -#define MCP_IODIR 0x00 /* init/reset: all ones */ -#define MCP_IPOL 0x01 -#define MCP_GPINTEN 0x02 -#define MCP_DEFVAL 0x03 -#define MCP_INTCON 0x04 -#define MCP_IOCON 0x05 -# define IOCON_MIRROR (1 << 6) -# define IOCON_SEQOP (1 << 5) -# define IOCON_HAEN (1 << 3) -# define IOCON_ODR (1 << 2) -# define IOCON_INTPOL (1 << 1) -# define IOCON_INTCC (1) -#define MCP_GPPU 0x06 -#define MCP_INTF 0x07 -#define MCP_INTCAP 0x08 -#define MCP_GPIO 0x09 -#define MCP_OLAT 0x0a - -struct mcp23s08; - -struct mcp23s08 { - u8 addr; - bool irq_active_high; - bool reg_shift; - - u16 cache[11]; - u16 irq_rise; - u16 irq_fall; - int irq; - bool irq_controller; - /* lock protects the cached values */ - struct mutex lock; - struct mutex irq_lock; - - struct gpio_chip chip; - - struct regmap *regmap; - struct device *dev; -}; - -static const struct regmap_config mcp23x08_regmap = { - .reg_bits = 8, - .val_bits = 8, - - .reg_stride = 1, - .max_register = MCP_OLAT, -}; - -static const struct regmap_config mcp23x17_regmap = { - .reg_bits = 8, - .val_bits = 16, - - .reg_stride = 2, - .max_register = MCP_OLAT << 1, - .val_format_endian = REGMAP_ENDIAN_LITTLE, -}; - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_SPI_MASTER - -static int mcp23sxx_spi_write(void *context, const void *data, size_t count) -{ - struct mcp23s08 *mcp = context; - struct spi_device *spi = to_spi_device(mcp->dev); - struct spi_message m; - struct spi_transfer t[2] = { { .tx_buf = &mcp->addr, .len = 1, }, - { .tx_buf = data, .len = count, }, }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - return spi_sync(spi, &m); -} - -static int mcp23sxx_spi_gather_write(void *context, - const void *reg, size_t reg_size, - const void *val, size_t val_size) -{ - struct mcp23s08 *mcp = context; - struct spi_device *spi = to_spi_device(mcp->dev); - struct spi_message m; - struct spi_transfer t[3] = { { .tx_buf = &mcp->addr, .len = 1, }, - { .tx_buf = reg, .len = reg_size, }, - { .tx_buf = val, .len = val_size, }, }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - spi_message_add_tail(&t[2], &m); - - return spi_sync(spi, &m); -} - -static int mcp23sxx_spi_read(void *context, const void *reg, size_t reg_size, - void *val, size_t val_size) -{ - struct mcp23s08 *mcp = context; - struct spi_device *spi = to_spi_device(mcp->dev); - u8 tx[2]; - - if (reg_size != 1) - return -EINVAL; - - tx[0] = mcp->addr | 0x01; - tx[1] = *((u8 *) reg); - - return spi_write_then_read(spi, tx, sizeof(tx), val, val_size); -} - -static const struct regmap_bus mcp23sxx_spi_regmap = { - .write = mcp23sxx_spi_write, - .gather_write = mcp23sxx_spi_gather_write, - .read = mcp23sxx_spi_read, -}; - -#endif /* CONFIG_SPI_MASTER */ - -static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) -{ - return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); -} - -static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) -{ - return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); -} - -static int mcp_update_cache(struct mcp23s08 *mcp) -{ - int ret, reg, i; - - for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { - ret = mcp_read(mcp, i, ®); - if (ret < 0) - return ret; - mcp->cache[i] = reg; - } - - return 0; -} - -/*----------------------------------------------------------------------*/ - -/* A given spi_device can represent up to eight mcp23sxx chips - * sharing the same chipselect but using different addresses - * (e.g. chips #0 and #3 might be populated, but not #1 or $2). - * Driver data holds all the per-chip data. - */ -struct mcp23s08_driver_data { - unsigned ngpio; - struct mcp23s08 *mcp[8]; - struct mcp23s08 chip[]; -}; - - -static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct mcp23s08 *mcp = gpiochip_get_data(chip); - int status; - - mutex_lock(&mcp->lock); - mcp->cache[MCP_IODIR] |= (1 << offset); - status = mcp_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); - mutex_unlock(&mcp->lock); - return status; -} - -static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) -{ - struct mcp23s08 *mcp = gpiochip_get_data(chip); - int status, ret; - - mutex_lock(&mcp->lock); - - /* REVISIT reading this clears any IRQ ... */ - ret = mcp_read(mcp, MCP_GPIO, &status); - if (ret < 0) - status = 0; - else { - mcp->cache[MCP_GPIO] = status; - status = !!(status & (1 << offset)); - } - mutex_unlock(&mcp->lock); - return status; -} - -static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) -{ - unsigned olat = mcp->cache[MCP_OLAT]; - - if (value) - olat |= mask; - else - olat &= ~mask; - mcp->cache[MCP_OLAT] = olat; - return mcp_write(mcp, MCP_OLAT, olat); -} - -static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mcp23s08 *mcp = gpiochip_get_data(chip); - unsigned mask = 1 << offset; - - mutex_lock(&mcp->lock); - __mcp23s08_set(mcp, mask, value); - mutex_unlock(&mcp->lock); -} - -static int -mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mcp23s08 *mcp = gpiochip_get_data(chip); - unsigned mask = 1 << offset; - int status; - - mutex_lock(&mcp->lock); - status = __mcp23s08_set(mcp, mask, value); - if (status == 0) { - mcp->cache[MCP_IODIR] &= ~mask; - status = mcp_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); - } - mutex_unlock(&mcp->lock); - return status; -} - -/*----------------------------------------------------------------------*/ -static irqreturn_t mcp23s08_irq(int irq, void *data) -{ - struct mcp23s08 *mcp = data; - int intcap, intf, i, gpio, gpio_orig, intcap_mask; - unsigned int child_irq; - bool intf_set, intcap_changed, gpio_bit_changed, - defval_changed, gpio_set; - - mutex_lock(&mcp->lock); - if (mcp_read(mcp, MCP_INTF, &intf) < 0) { - mutex_unlock(&mcp->lock); - return IRQ_HANDLED; - } - - mcp->cache[MCP_INTF] = intf; - - if (mcp_read(mcp, MCP_INTCAP, &intcap) < 0) { - mutex_unlock(&mcp->lock); - return IRQ_HANDLED; - } - - mcp->cache[MCP_INTCAP] = intcap; - - /* This clears the interrupt(configurable on S18) */ - if (mcp_read(mcp, MCP_GPIO, &gpio) < 0) { - mutex_unlock(&mcp->lock); - return IRQ_HANDLED; - } - gpio_orig = mcp->cache[MCP_GPIO]; - mcp->cache[MCP_GPIO] = gpio; - mutex_unlock(&mcp->lock); - - if (mcp->cache[MCP_INTF] == 0) { - /* There is no interrupt pending */ - return IRQ_HANDLED; - } - - dev_dbg(mcp->chip.parent, - "intcap 0x%04X intf 0x%04X gpio_orig 0x%04X gpio 0x%04X\n", - intcap, intf, gpio_orig, gpio); - - for (i = 0; i < mcp->chip.ngpio; i++) { - /* We must check all of the inputs on the chip, - * otherwise we may not notice a change on >=2 pins. - * - * On at least the mcp23s17, INTCAP is only updated - * one byte at a time(INTCAPA and INTCAPB are - * not written to at the same time - only on a per-bank - * basis). - * - * INTF only contains the single bit that caused the - * interrupt per-bank. On the mcp23s17, there is - * INTFA and INTFB. If two pins are changed on the A - * side at the same time, INTF will only have one bit - * set. If one pin on the A side and one pin on the B - * side are changed at the same time, INTF will have - * two bits set. Thus, INTF can't be the only check - * to see if the input has changed. - */ - - intf_set = BIT(i) & mcp->cache[MCP_INTF]; - if (i < 8 && intf_set) - intcap_mask = 0x00FF; - else if (i >= 8 && intf_set) - intcap_mask = 0xFF00; - else - intcap_mask = 0x00; - - intcap_changed = (intcap_mask & - (BIT(i) & mcp->cache[MCP_INTCAP])) != - (intcap_mask & (BIT(i) & gpio_orig)); - gpio_set = BIT(i) & mcp->cache[MCP_GPIO]; - gpio_bit_changed = (BIT(i) & gpio_orig) != - (BIT(i) & mcp->cache[MCP_GPIO]); - defval_changed = (BIT(i) & mcp->cache[MCP_INTCON]) && - ((BIT(i) & mcp->cache[MCP_GPIO]) != - (BIT(i) & mcp->cache[MCP_DEFVAL])); - - if (((gpio_bit_changed || intcap_changed) && - (BIT(i) & mcp->irq_rise) && gpio_set) || - ((gpio_bit_changed || intcap_changed) && - (BIT(i) & mcp->irq_fall) && !gpio_set) || - defval_changed) { - child_irq = irq_find_mapping(mcp->chip.irqdomain, i); - handle_nested_irq(child_irq); - } - } - - return IRQ_HANDLED; -} - -static void mcp23s08_irq_mask(struct irq_data *data) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - struct mcp23s08 *mcp = gpiochip_get_data(gc); - unsigned int pos = data->hwirq; - - mcp->cache[MCP_GPINTEN] &= ~BIT(pos); -} - -static void mcp23s08_irq_unmask(struct irq_data *data) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - struct mcp23s08 *mcp = gpiochip_get_data(gc); - unsigned int pos = data->hwirq; - - mcp->cache[MCP_GPINTEN] |= BIT(pos); -} - -static int mcp23s08_irq_set_type(struct irq_data *data, unsigned int type) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - struct mcp23s08 *mcp = gpiochip_get_data(gc); - unsigned int pos = data->hwirq; - int status = 0; - - if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { - mcp->cache[MCP_INTCON] &= ~BIT(pos); - mcp->irq_rise |= BIT(pos); - mcp->irq_fall |= BIT(pos); - } else if (type & IRQ_TYPE_EDGE_RISING) { - mcp->cache[MCP_INTCON] &= ~BIT(pos); - mcp->irq_rise |= BIT(pos); - mcp->irq_fall &= ~BIT(pos); - } else if (type & IRQ_TYPE_EDGE_FALLING) { - mcp->cache[MCP_INTCON] &= ~BIT(pos); - mcp->irq_rise &= ~BIT(pos); - mcp->irq_fall |= BIT(pos); - } else if (type & IRQ_TYPE_LEVEL_HIGH) { - mcp->cache[MCP_INTCON] |= BIT(pos); - mcp->cache[MCP_DEFVAL] &= ~BIT(pos); - } else if (type & IRQ_TYPE_LEVEL_LOW) { - mcp->cache[MCP_INTCON] |= BIT(pos); - mcp->cache[MCP_DEFVAL] |= BIT(pos); - } else - return -EINVAL; - - return status; -} - -static void mcp23s08_irq_bus_lock(struct irq_data *data) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - struct mcp23s08 *mcp = gpiochip_get_data(gc); - - mutex_lock(&mcp->irq_lock); -} - -static void mcp23s08_irq_bus_unlock(struct irq_data *data) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - struct mcp23s08 *mcp = gpiochip_get_data(gc); - - mutex_lock(&mcp->lock); - mcp_write(mcp, MCP_GPINTEN, mcp->cache[MCP_GPINTEN]); - mcp_write(mcp, MCP_DEFVAL, mcp->cache[MCP_DEFVAL]); - mcp_write(mcp, MCP_INTCON, mcp->cache[MCP_INTCON]); - mutex_unlock(&mcp->lock); - mutex_unlock(&mcp->irq_lock); -} - -static struct irq_chip mcp23s08_irq_chip = { - .name = "gpio-mcp23xxx", - .irq_mask = mcp23s08_irq_mask, - .irq_unmask = mcp23s08_irq_unmask, - .irq_set_type = mcp23s08_irq_set_type, - .irq_bus_lock = mcp23s08_irq_bus_lock, - .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock, -}; - -static int mcp23s08_irq_setup(struct mcp23s08 *mcp) -{ - struct gpio_chip *chip = &mcp->chip; - int err; - unsigned long irqflags = IRQF_ONESHOT | IRQF_SHARED; - - mutex_init(&mcp->irq_lock); - - if (mcp->irq_active_high) - irqflags |= IRQF_TRIGGER_HIGH; - else - irqflags |= IRQF_TRIGGER_LOW; - - err = devm_request_threaded_irq(chip->parent, mcp->irq, NULL, - mcp23s08_irq, - irqflags, dev_name(chip->parent), mcp); - if (err != 0) { - dev_err(chip->parent, "unable to request IRQ#%d: %d\n", - mcp->irq, err); - return err; - } - - err = gpiochip_irqchip_add_nested(chip, - &mcp23s08_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (err) { - dev_err(chip->parent, - "could not connect irqchip to gpiochip: %d\n", err); - return err; - } - - gpiochip_set_nested_irqchip(chip, - &mcp23s08_irq_chip, - mcp->irq); - - return 0; -} - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_DEBUG_FS - -#include <linux/seq_file.h> - -/* - * This shows more info than the generic gpio dump code: - * pullups, deglitching, open drain drive. - */ -static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - struct mcp23s08 *mcp; - char bank; - int t; - unsigned mask; - - mcp = gpiochip_get_data(chip); - - /* NOTE: we only handle one bank for now ... */ - bank = '0' + ((mcp->addr >> 1) & 0x7); - - mutex_lock(&mcp->lock); - t = mcp_update_cache(mcp); - if (t < 0) { - seq_printf(s, " I/O ERROR %d\n", t); - goto done; - } - - for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { - const char *label; - - label = gpiochip_is_requested(chip, t); - if (!label) - continue; - - seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", - chip->base + t, bank, t, label, - (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", - (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", - (mcp->cache[MCP_GPPU] & mask) ? "up" : " "); - /* NOTE: ignoring the irq-related registers */ - seq_puts(s, "\n"); - } -done: - mutex_unlock(&mcp->lock); -} - -#else -#define mcp23s08_dbg_show NULL -#endif - -/*----------------------------------------------------------------------*/ - -static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, - void *data, unsigned addr, unsigned type, - struct mcp23s08_platform_data *pdata, int cs) -{ - int status, ret; - bool mirror = false; - - mutex_init(&mcp->lock); - - mcp->dev = dev; - mcp->addr = addr; - mcp->irq_active_high = false; - - mcp->chip.direction_input = mcp23s08_direction_input; - mcp->chip.get = mcp23s08_get; - mcp->chip.direction_output = mcp23s08_direction_output; - mcp->chip.set = mcp23s08_set; - mcp->chip.dbg_show = mcp23s08_dbg_show; -#ifdef CONFIG_OF_GPIO - mcp->chip.of_gpio_n_cells = 2; - mcp->chip.of_node = dev->of_node; -#endif - - switch (type) { -#ifdef CONFIG_SPI_MASTER - case MCP_TYPE_S08: - mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, - &mcp23x08_regmap); - mcp->reg_shift = 0; - mcp->chip.ngpio = 8; - mcp->chip.label = "mcp23s08"; - break; - - case MCP_TYPE_S17: - mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, - &mcp23x17_regmap); - mcp->reg_shift = 1; - mcp->chip.ngpio = 16; - mcp->chip.label = "mcp23s17"; - break; - - case MCP_TYPE_S18: - mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, - &mcp23x17_regmap); - mcp->reg_shift = 1; - mcp->chip.ngpio = 16; - mcp->chip.label = "mcp23s18"; - break; -#endif /* CONFIG_SPI_MASTER */ - -#if IS_ENABLED(CONFIG_I2C) - case MCP_TYPE_008: - mcp->regmap = devm_regmap_init_i2c(data, &mcp23x08_regmap); - mcp->reg_shift = 0; - mcp->chip.ngpio = 8; - mcp->chip.label = "mcp23008"; - break; - - case MCP_TYPE_017: - mcp->regmap = devm_regmap_init_i2c(data, &mcp23x17_regmap); - mcp->reg_shift = 1; - mcp->chip.ngpio = 16; - mcp->chip.label = "mcp23017"; - break; -#endif /* CONFIG_I2C */ - - default: - dev_err(dev, "invalid device type (%d)\n", type); - return -EINVAL; - } - - if (IS_ERR(mcp->regmap)) - return PTR_ERR(mcp->regmap); - - mcp->chip.base = pdata->base; - mcp->chip.can_sleep = true; - mcp->chip.parent = dev; - mcp->chip.owner = THIS_MODULE; - - /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, - * and MCP_IOCON.HAEN = 1, so we work with all chips. - */ - - ret = mcp_read(mcp, MCP_IOCON, &status); - if (ret < 0) - goto fail; - - mcp->irq_controller = pdata->irq_controller; - if (mcp->irq && mcp->irq_controller) { - mcp->irq_active_high = - of_property_read_bool(mcp->chip.parent->of_node, - "microchip,irq-active-high"); - - mirror = pdata->mirror; - } - - if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror || - mcp->irq_active_high) { - /* mcp23s17 has IOCON twice, make sure they are in sync */ - status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); - status |= IOCON_HAEN | (IOCON_HAEN << 8); - if (mcp->irq_active_high) - status |= IOCON_INTPOL | (IOCON_INTPOL << 8); - else - status &= ~(IOCON_INTPOL | (IOCON_INTPOL << 8)); - - if (mirror) - status |= IOCON_MIRROR | (IOCON_MIRROR << 8); - - if (type == MCP_TYPE_S18) - status |= IOCON_INTCC | (IOCON_INTCC << 8); - - ret = mcp_write(mcp, MCP_IOCON, status); - if (ret < 0) - goto fail; - } - - /* configure ~100K pullups */ - ret = mcp_write(mcp, MCP_GPPU, pdata->chip[cs].pullups); - if (ret < 0) - goto fail; - - ret = mcp_update_cache(mcp); - if (ret < 0) - goto fail; - - /* disable inverter on input */ - if (mcp->cache[MCP_IPOL] != 0) { - mcp->cache[MCP_IPOL] = 0; - ret = mcp_write(mcp, MCP_IPOL, 0); - if (ret < 0) - goto fail; - } - - /* disable irqs */ - if (mcp->cache[MCP_GPINTEN] != 0) { - mcp->cache[MCP_GPINTEN] = 0; - ret = mcp_write(mcp, MCP_GPINTEN, 0); - if (ret < 0) - goto fail; - } - - ret = gpiochip_add_data(&mcp->chip, mcp); - if (ret < 0) - goto fail; - - if (mcp->irq && mcp->irq_controller) { - ret = mcp23s08_irq_setup(mcp); - if (ret) - goto fail; - } -fail: - if (ret < 0) - dev_dbg(dev, "can't setup chip %d, --> %d\n", addr, ret); - return ret; -} - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_OF -#ifdef CONFIG_SPI_MASTER -static const struct of_device_id mcp23s08_spi_of_match[] = { - { - .compatible = "microchip,mcp23s08", - .data = (void *) MCP_TYPE_S08, - }, - { - .compatible = "microchip,mcp23s17", - .data = (void *) MCP_TYPE_S17, - }, - { - .compatible = "microchip,mcp23s18", - .data = (void *) MCP_TYPE_S18, - }, -/* NOTE: The use of the mcp prefix is deprecated and will be removed. */ - { - .compatible = "mcp,mcp23s08", - .data = (void *) MCP_TYPE_S08, - }, - { - .compatible = "mcp,mcp23s17", - .data = (void *) MCP_TYPE_S17, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match); -#endif - -#if IS_ENABLED(CONFIG_I2C) -static const struct of_device_id mcp23s08_i2c_of_match[] = { - { - .compatible = "microchip,mcp23008", - .data = (void *) MCP_TYPE_008, - }, - { - .compatible = "microchip,mcp23017", - .data = (void *) MCP_TYPE_017, - }, -/* NOTE: The use of the mcp prefix is deprecated and will be removed. */ - { - .compatible = "mcp,mcp23008", - .data = (void *) MCP_TYPE_008, - }, - { - .compatible = "mcp,mcp23017", - .data = (void *) MCP_TYPE_017, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match); -#endif -#endif /* CONFIG_OF */ - - -#if IS_ENABLED(CONFIG_I2C) - -static int mcp230xx_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct mcp23s08_platform_data *pdata, local_pdata; - struct mcp23s08 *mcp; - int status; - const struct of_device_id *match; - - match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match), - &client->dev); - if (match) { - pdata = &local_pdata; - pdata->base = -1; - pdata->chip[0].pullups = 0; - pdata->irq_controller = of_property_read_bool( - client->dev.of_node, - "interrupt-controller"); - pdata->mirror = of_property_read_bool(client->dev.of_node, - "microchip,irq-mirror"); - client->irq = irq_of_parse_and_map(client->dev.of_node, 0); - } else { - pdata = dev_get_platdata(&client->dev); - if (!pdata) { - pdata = devm_kzalloc(&client->dev, - sizeof(struct mcp23s08_platform_data), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - pdata->base = -1; - } - } - - mcp = kzalloc(sizeof(*mcp), GFP_KERNEL); - if (!mcp) - return -ENOMEM; - - mcp->irq = client->irq; - status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, - id->driver_data, pdata, 0); - if (status) - goto fail; - - i2c_set_clientdata(client, mcp); - - return 0; - -fail: - kfree(mcp); - - return status; -} - -static int mcp230xx_remove(struct i2c_client *client) -{ - struct mcp23s08 *mcp = i2c_get_clientdata(client); - - gpiochip_remove(&mcp->chip); - kfree(mcp); - - return 0; -} - -static const struct i2c_device_id mcp230xx_id[] = { - { "mcp23008", MCP_TYPE_008 }, - { "mcp23017", MCP_TYPE_017 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, mcp230xx_id); - -static struct i2c_driver mcp230xx_driver = { - .driver = { - .name = "mcp230xx", - .of_match_table = of_match_ptr(mcp23s08_i2c_of_match), - }, - .probe = mcp230xx_probe, - .remove = mcp230xx_remove, - .id_table = mcp230xx_id, -}; - -static int __init mcp23s08_i2c_init(void) -{ - return i2c_add_driver(&mcp230xx_driver); -} - -static void mcp23s08_i2c_exit(void) -{ - i2c_del_driver(&mcp230xx_driver); -} - -#else - -static int __init mcp23s08_i2c_init(void) { return 0; } -static void mcp23s08_i2c_exit(void) { } - -#endif /* CONFIG_I2C */ - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_SPI_MASTER - -static int mcp23s08_probe(struct spi_device *spi) -{ - struct mcp23s08_platform_data *pdata, local_pdata; - unsigned addr; - int chips = 0; - struct mcp23s08_driver_data *data; - int status, type; - unsigned ngpio = 0; - const struct of_device_id *match; - u32 spi_present_mask = 0; - - match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev); - if (match) { - type = (int)(uintptr_t)match->data; - status = of_property_read_u32(spi->dev.of_node, - "microchip,spi-present-mask", &spi_present_mask); - if (status) { - status = of_property_read_u32(spi->dev.of_node, - "mcp,spi-present-mask", &spi_present_mask); - if (status) { - dev_err(&spi->dev, - "DT has no spi-present-mask\n"); - return -ENODEV; - } - } - if ((spi_present_mask <= 0) || (spi_present_mask >= 256)) { - dev_err(&spi->dev, "invalid spi-present-mask\n"); - return -ENODEV; - } - - pdata = &local_pdata; - pdata->base = -1; - for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { - pdata->chip[addr].pullups = 0; - if (spi_present_mask & (1 << addr)) - chips++; - } - pdata->irq_controller = of_property_read_bool( - spi->dev.of_node, - "interrupt-controller"); - pdata->mirror = of_property_read_bool(spi->dev.of_node, - "microchip,irq-mirror"); - } else { - type = spi_get_device_id(spi)->driver_data; - pdata = dev_get_platdata(&spi->dev); - if (!pdata) { - pdata = devm_kzalloc(&spi->dev, - sizeof(struct mcp23s08_platform_data), - GFP_KERNEL); - pdata->base = -1; - } - - for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { - if (!pdata->chip[addr].is_present) - continue; - chips++; - if ((type == MCP_TYPE_S08) && (addr > 3)) { - dev_err(&spi->dev, - "mcp23s08 only supports address 0..3\n"); - return -EINVAL; - } - spi_present_mask |= 1 << addr; - } - } - - if (!chips) - return -ENODEV; - - data = devm_kzalloc(&spi->dev, - sizeof(*data) + chips * sizeof(struct mcp23s08), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - spi_set_drvdata(spi, data); - - spi->irq = irq_of_parse_and_map(spi->dev.of_node, 0); - - for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { - if (!(spi_present_mask & (1 << addr))) - continue; - chips--; - data->mcp[addr] = &data->chip[chips]; - data->mcp[addr]->irq = spi->irq; - status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, - 0x40 | (addr << 1), type, pdata, - addr); - if (status < 0) - goto fail; - - if (pdata->base != -1) - pdata->base += data->mcp[addr]->chip.ngpio; - ngpio += data->mcp[addr]->chip.ngpio; - } - data->ngpio = ngpio; - - /* NOTE: these chips have a relatively sane IRQ framework, with - * per-signal masking and level/edge triggering. It's not yet - * handled here... - */ - - return 0; - -fail: - for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { - - if (!data->mcp[addr]) - continue; - gpiochip_remove(&data->mcp[addr]->chip); - } - return status; -} - -static int mcp23s08_remove(struct spi_device *spi) -{ - struct mcp23s08_driver_data *data = spi_get_drvdata(spi); - unsigned addr; - - for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { - - if (!data->mcp[addr]) - continue; - - gpiochip_remove(&data->mcp[addr]->chip); - } - - return 0; -} - -static const struct spi_device_id mcp23s08_ids[] = { - { "mcp23s08", MCP_TYPE_S08 }, - { "mcp23s17", MCP_TYPE_S17 }, - { "mcp23s18", MCP_TYPE_S18 }, - { }, -}; -MODULE_DEVICE_TABLE(spi, mcp23s08_ids); - -static struct spi_driver mcp23s08_driver = { - .probe = mcp23s08_probe, - .remove = mcp23s08_remove, - .id_table = mcp23s08_ids, - .driver = { - .name = "mcp23s08", - .of_match_table = of_match_ptr(mcp23s08_spi_of_match), - }, -}; - -static int __init mcp23s08_spi_init(void) -{ - return spi_register_driver(&mcp23s08_driver); -} - -static void mcp23s08_spi_exit(void) -{ - spi_unregister_driver(&mcp23s08_driver); -} - -#else - -static int __init mcp23s08_spi_init(void) { return 0; } -static void mcp23s08_spi_exit(void) { } - -#endif /* CONFIG_SPI_MASTER */ - -/*----------------------------------------------------------------------*/ - -static int __init mcp23s08_init(void) -{ - int ret; - - ret = mcp23s08_spi_init(); - if (ret) - goto spi_fail; - - ret = mcp23s08_i2c_init(); - if (ret) - goto i2c_fail; - - return 0; - - i2c_fail: - mcp23s08_spi_exit(); - spi_fail: - return ret; -} -/* register after spi/i2c postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(mcp23s08_init); - -static void __exit mcp23s08_exit(void) -{ - mcp23s08_spi_exit(); - mcp23s08_i2c_exit(); -} -module_exit(mcp23s08_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 9dbdc3672f5e..ec8560298805 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -11,7 +11,6 @@ #include <linux/bitops.h> #include <linux/gpio/driver.h> -#include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 78896a869fd9..74fdce096c26 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -385,14 +385,18 @@ static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) return ret; } -static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, - unsigned int irq_start, unsigned int num) +static int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, + unsigned int irq_start, + unsigned int num) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base, handle_simple_irq); + if (!gc) + return -ENOMEM; + gc->private = chip; ct = gc->chip_types; @@ -404,6 +408,8 @@ static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST | IRQ_NOPROBE, 0); + + return 0; } static int ioh_gpio_probe(struct pci_dev *pdev, @@ -468,7 +474,11 @@ static int ioh_gpio_probe(struct pci_dev *pdev, goto err_gpiochip_add; } chip->irq_base = irq_base; - ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]); + + ret = ioh_gpio_alloc_generic_chip(chip, + irq_base, num_ports[j]); + if (ret) + goto err_gpiochip_add; } chip = chip_save; diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index c6dadac70593..a6565e128f9e 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -3,6 +3,7 @@ * * Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com> * Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org> + * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -27,10 +28,15 @@ #define GPIO_MOCKUP_NAME "gpio-mockup" #define GPIO_MOCKUP_MAX_GC 10 +/* + * We're storing two values per chip: the GPIO base and the number + * of GPIO lines. + */ +#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2) enum { - DIR_IN = 0, - DIR_OUT, + GPIO_MOCKUP_DIR_OUT = 0, + GPIO_MOCKUP_DIR_IN = 1, }; /* @@ -41,6 +47,7 @@ enum { struct gpio_mockup_line_status { int dir; bool value; + bool irq_enabled; }; struct gpio_mockup_irq_context { @@ -61,7 +68,7 @@ struct gpio_mockup_dbgfs_private { int offset; }; -static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1]; +static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; static int gpio_mockup_params_nr; module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); @@ -93,7 +100,7 @@ static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, struct gpio_mockup_chip *chip = gpiochip_get_data(gc); gpio_mockup_set(gc, offset, value); - chip->lines[offset].dir = DIR_OUT; + chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT; return 0; } @@ -102,7 +109,7 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - chip->lines[offset].dir = DIR_IN; + chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN; return 0; } @@ -121,7 +128,7 @@ static int gpio_mockup_name_lines(struct device *dev, char **names; int i; - names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL); + names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL); if (!names) return -ENOMEM; @@ -142,12 +149,21 @@ static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset) return chip->irq_base + offset; } -/* - * While we should generally support irqmask and irqunmask, this driver is - * for testing purposes only so we don't care. - */ -static void gpio_mockup_irqmask(struct irq_data *d) { } -static void gpio_mockup_irqunmask(struct irq_data *d) { } +static void gpio_mockup_irqmask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + chip->lines[data->irq - gc->irq_base].irq_enabled = false; +} + +static void gpio_mockup_irqunmask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + chip->lines[data->irq - gc->irq_base].irq_enabled = true; +} static struct irq_chip gpio_mockup_irqchip = { .name = GPIO_MOCKUP_NAME, @@ -178,6 +194,7 @@ static int gpio_mockup_irqchip_setup(struct device *dev, for (i = 0; i < gc->ngpio; i++) { irq_set_chip(irq_base + i, gc->irqchip); + irq_set_chip_data(irq_base + i, gc); irq_set_handler(irq_base + i, &handle_simple_irq); irq_modify_status(irq_base + i, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); @@ -197,8 +214,13 @@ static ssize_t gpio_mockup_event_write(struct file *file, struct seq_file *sfile; struct gpio_desc *desc; struct gpio_chip *gc; - int val; - char buf; + int rv, val; + + rv = kstrtoint_from_user(usr_buf, size, 0, &val); + if (rv) + return rv; + if (val != 0 && val != 1) + return -EINVAL; sfile = file->private_data; priv = sfile->private; @@ -206,19 +228,11 @@ static ssize_t gpio_mockup_event_write(struct file *file, chip = priv->chip; gc = &chip->gc; - if (copy_from_user(&buf, usr_buf, 1)) - return -EFAULT; - - if (buf == '0') - val = 0; - else if (buf == '1') - val = 1; - else - return -EINVAL; - - gpiod_set_value_cansleep(desc, val); - priv->chip->irq_ctx.irq = gc->irq_base + priv->offset; - irq_work_queue(&priv->chip->irq_ctx.work); + if (chip->lines[priv->offset].irq_enabled) { + gpiod_set_value_cansleep(desc, val); + priv->chip->irq_ctx.irq = gc->irq_base + priv->offset; + irq_work_queue(&priv->chip->irq_ctx.work); + } return size; } @@ -294,8 +308,8 @@ static int gpio_mockup_add(struct device *dev, gc->get_direction = gpio_mockup_get_direction; gc->to_irq = gpio_mockup_to_irq; - chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio, - GFP_KERNEL); + chip->lines = devm_kcalloc(dev, gc->ngpio, + sizeof(*chip->lines), GFP_KERNEL); if (!chip->lines) return -ENOMEM; @@ -321,23 +335,24 @@ static int gpio_mockup_add(struct device *dev, static int gpio_mockup_probe(struct platform_device *pdev) { - struct gpio_mockup_chip *chips; + int ret, i, base, ngpio, num_chips; struct device *dev = &pdev->dev; - int ret, i, base, ngpio; + struct gpio_mockup_chip *chips; char *chip_name; - if (gpio_mockup_params_nr < 2) + if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) return -EINVAL; - chips = devm_kzalloc(dev, - sizeof(*chips) * (gpio_mockup_params_nr >> 1), - GFP_KERNEL); + /* Each chip is described by two values. */ + num_chips = gpio_mockup_params_nr / 2; + + chips = devm_kcalloc(dev, num_chips, sizeof(*chips), GFP_KERNEL); if (!chips) return -ENOMEM; platform_set_drvdata(pdev, chips); - for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { + for (i = 0; i < num_chips; i++) { base = gpio_mockup_ranges[i * 2]; if (base == -1) @@ -355,18 +370,16 @@ static int gpio_mockup_probe(struct platform_device *pdev) ret = gpio_mockup_add(dev, &chips[i], chip_name, base, ngpio); } else { - ret = -1; + ret = -EINVAL; } if (ret) { - dev_err(dev, "gpio<%d..%d> add failed\n", - base, base < 0 ? ngpio : base + ngpio); + dev_err(dev, + "adding gpiochip failed: %d (base: %d, ngpio: %d)\n", + ret, base, base < 0 ? ngpio : base + ngpio); return ret; } - - dev_info(dev, "gpio<%d..%d> add successful!", - base, base + ngpio); } return 0; @@ -420,5 +433,6 @@ module_exit(mock_device_exit); MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>"); MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>"); +MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>"); MODULE_DESCRIPTION("GPIO Testing driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index c83ea68be792..e338c3743562 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -33,21 +33,23 @@ * interrupts. */ +#include <linux/bitops.h> +#include <linux/clk.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/gpio.h> +#include <linux/init.h> +#include <linux/io.h> #include <linux/irq.h> -#include <linux/slab.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> -#include <linux/io.h> -#include <linux/of_irq.h> +#include <linux/mfd/syscon.h> #include <linux/of_device.h> -#include <linux/pwm.h> -#include <linux/clk.h> +#include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> -#include <linux/irqchip/chained_irq.h> #include <linux/platform_device.h> -#include <linux/bitops.h> +#include <linux/pwm.h> +#include <linux/regmap.h> +#include <linux/slab.h> #include "gpiolib.h" @@ -87,6 +89,7 @@ #define MVEBU_GPIO_SOC_VARIANT_ORION 0x1 #define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2 #define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3 +#define MVEBU_GPIO_SOC_VARIANT_A8K 0x4 #define MVEBU_MAX_GPIO_PER_BANK 32 @@ -106,9 +109,9 @@ struct mvebu_pwm { struct mvebu_gpio_chip { struct gpio_chip chip; - spinlock_t lock; - void __iomem *membase; - void __iomem *percpu_membase; + struct regmap *regs; + u32 offset; + struct regmap *percpu_regs; int irqbase; struct irq_domain *domain; int soc_variant; @@ -130,92 +133,152 @@ struct mvebu_gpio_chip { * Functions returning addresses of individual registers for a given * GPIO controller. */ -static void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_OUT_OFF; -} -static void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip) +static void mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip, + struct regmap **map, unsigned int *offset) { - return mvchip->membase + GPIO_BLINK_EN_OFF; -} + int cpu; -static void __iomem *mvebu_gpioreg_blink_counter_select(struct mvebu_gpio_chip - *mvchip) -{ - return mvchip->membase + GPIO_BLINK_CNT_SELECT_OFF; + switch (mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + case MVEBU_GPIO_SOC_VARIANT_MV78200: + case MVEBU_GPIO_SOC_VARIANT_A8K: + *map = mvchip->regs; + *offset = GPIO_EDGE_CAUSE_OFF + mvchip->offset; + break; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + cpu = smp_processor_id(); + *map = mvchip->percpu_regs; + *offset = GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); + break; + default: + BUG(); + } } -static void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) +static u32 +mvebu_gpio_read_edge_cause(struct mvebu_gpio_chip *mvchip) { - return mvchip->membase + GPIO_IO_CONF_OFF; -} + struct regmap *map; + unsigned int offset; + u32 val; -static void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_IN_POL_OFF; + mvebu_gpioreg_edge_cause(mvchip, &map, &offset); + regmap_read(map, offset, &val); + + return val; } -static void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) +static void +mvebu_gpio_write_edge_cause(struct mvebu_gpio_chip *mvchip, u32 val) { - return mvchip->membase + GPIO_DATA_IN_OFF; + struct regmap *map; + unsigned int offset; + + mvebu_gpioreg_edge_cause(mvchip, &map, &offset); + regmap_write(map, offset, val); } -static void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) +static inline void +mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip, + struct regmap **map, unsigned int *offset) { int cpu; switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: + case MVEBU_GPIO_SOC_VARIANT_A8K: + *map = mvchip->regs; + *offset = GPIO_EDGE_MASK_OFF + mvchip->offset; + break; case MVEBU_GPIO_SOC_VARIANT_MV78200: - return mvchip->membase + GPIO_EDGE_CAUSE_OFF; + cpu = smp_processor_id(); + *map = mvchip->regs; + *offset = GPIO_EDGE_MASK_MV78200_OFF(cpu); + break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + - GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); + *map = mvchip->percpu_regs; + *offset = GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); + break; default: BUG(); } } -static void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) +static u32 +mvebu_gpio_read_edge_mask(struct mvebu_gpio_chip *mvchip) { - int cpu; + struct regmap *map; + unsigned int offset; + u32 val; - switch (mvchip->soc_variant) { - case MVEBU_GPIO_SOC_VARIANT_ORION: - return mvchip->membase + GPIO_EDGE_MASK_OFF; - case MVEBU_GPIO_SOC_VARIANT_MV78200: - cpu = smp_processor_id(); - return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu); - case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: - cpu = smp_processor_id(); - return mvchip->percpu_membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); - default: - BUG(); - } + mvebu_gpioreg_edge_mask(mvchip, &map, &offset); + regmap_read(map, offset, &val); + + return val; } -static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip) +static void +mvebu_gpio_write_edge_mask(struct mvebu_gpio_chip *mvchip, u32 val) +{ + struct regmap *map; + unsigned int offset; + + mvebu_gpioreg_edge_mask(mvchip, &map, &offset); + regmap_write(map, offset, val); +} + +static void +mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip, + struct regmap **map, unsigned int *offset) { int cpu; switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - return mvchip->membase + GPIO_LEVEL_MASK_OFF; + case MVEBU_GPIO_SOC_VARIANT_A8K: + *map = mvchip->regs; + *offset = GPIO_LEVEL_MASK_OFF + mvchip->offset; + break; case MVEBU_GPIO_SOC_VARIANT_MV78200: cpu = smp_processor_id(); - return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu); + *map = mvchip->regs; + *offset = GPIO_LEVEL_MASK_MV78200_OFF(cpu); + break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); + *map = mvchip->percpu_regs; + *offset = GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); + break; default: BUG(); } } +static u32 +mvebu_gpio_read_level_mask(struct mvebu_gpio_chip *mvchip) +{ + struct regmap *map; + unsigned int offset; + u32 val; + + mvebu_gpioreg_level_mask(mvchip, &map, &offset); + regmap_read(map, offset, &val); + + return val; +} + +static void +mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) +{ + struct regmap *map; + unsigned int offset; + + mvebu_gpioreg_level_mask(mvchip, &map, &offset); + regmap_write(map, offset, val); +} + /* * Functions returning addresses of individual registers for a given * PWM controller. @@ -236,17 +299,9 @@ static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; - u32 u; - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_out(mvchip)); - if (value) - u |= BIT(pin); - else - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_out(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, + BIT(pin), value ? BIT(pin) : 0); } static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) @@ -254,11 +309,18 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 u; - if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin)) { - u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^ - readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); + + if (u & BIT(pin)) { + u32 data_in, in_pol; + + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, + &data_in); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, + &in_pol); + u = data_in ^ in_pol; } else { - u = readl_relaxed(mvebu_gpioreg_out(mvchip)); + regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &u); } return (u >> pin) & 1; @@ -268,25 +330,15 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; - u32 u; - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_blink(mvchip)); - if (value) - u |= BIT(pin); - else - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_blink(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + BIT(pin), value ? BIT(pin) : 0); } static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; int ret; - u32 u; /* * Check with the pinctrl driver whether this pin is usable as @@ -296,11 +348,8 @@ static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) if (ret) return ret; - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - u |= BIT(pin); - writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, + BIT(pin), BIT(pin)); return 0; } @@ -309,9 +358,7 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; int ret; - u32 u; /* * Check with the pinctrl driver whether this pin is usable as @@ -324,11 +371,8 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, mvebu_gpio_blink(chip, pin, 0); mvebu_gpio_set(chip, pin, value); - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, + BIT(pin), 0); return 0; } @@ -350,7 +394,7 @@ static void mvebu_gpio_irq_ack(struct irq_data *d) u32 mask = d->mask; irq_gc_lock(gc); - writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip)); + mvebu_gpio_write_edge_cause(mvchip, ~mask); irq_gc_unlock(gc); } @@ -363,8 +407,7 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv &= ~mask; - - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); + mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -377,7 +420,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv |= mask; - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); + mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -390,7 +433,7 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv &= ~mask; - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); + mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -403,7 +446,7 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv |= mask; - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); + mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -443,8 +486,8 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) pin = d->hwirq; - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin); - if (!u) + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); + if ((u & BIT(pin)) == 0) return -EINVAL; type &= IRQ_TYPE_SENSE_MASK; @@ -462,31 +505,35 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) switch (type) { case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: - u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + regmap_update_bits(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + BIT(pin), 0); break; case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: - u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - u |= BIT(pin); - writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + regmap_update_bits(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + BIT(pin), BIT(pin)); break; case IRQ_TYPE_EDGE_BOTH: { - u32 v; + u32 data_in, in_pol, val; - v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^ - readl_relaxed(mvebu_gpioreg_data_in(mvchip)); + regmap_read(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, &in_pol); + regmap_read(mvchip->regs, + GPIO_DATA_IN_OFF + mvchip->offset, &data_in); /* * set initial polarity based on current input level */ - u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - if (v & BIT(pin)) - u |= BIT(pin); /* falling */ + if ((data_in ^ in_pol) & BIT(pin)) + val = BIT(pin); /* falling */ else - u &= ~BIT(pin); /* rising */ - writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + val = 0; /* raising */ + + regmap_update_bits(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + BIT(pin), val); break; } } @@ -497,7 +544,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) { struct mvebu_gpio_chip *mvchip = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); - u32 cause, type; + u32 cause, type, data_in, level_mask, edge_cause, edge_mask; int i; if (mvchip == NULL) @@ -505,10 +552,12 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) chained_irq_enter(chip, desc); - cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) & - readl_relaxed(mvebu_gpioreg_level_mask(mvchip)); - cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) & - readl_relaxed(mvebu_gpioreg_edge_mask(mvchip)); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in); + level_mask = mvebu_gpio_read_level_mask(mvchip); + edge_cause = mvebu_gpio_read_edge_cause(mvchip); + edge_mask = mvebu_gpio_read_edge_mask(mvchip); + + cause = (data_in ^ level_mask) | (edge_cause & edge_mask); for (i = 0; i < mvchip->chip.ngpio; i++) { int irq; @@ -523,9 +572,13 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) /* Swap polarity (race with GPIO line) */ u32 polarity; - polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + regmap_read(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + &polarity); polarity ^= BIT(i); - writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip)); + regmap_write(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + polarity); } generic_handle_irq(irq); @@ -628,7 +681,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, state->period = 1; } - u = readl_relaxed(mvebu_gpioreg_blink(mvchip)); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u); if (u) state->enabled = true; else @@ -691,8 +744,8 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm = mvchip->mvpwm; - mvpwm->blink_select = - readl_relaxed(mvebu_gpioreg_blink_counter_select(mvchip)); + regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, + &mvpwm->blink_select); mvpwm->blink_on_duration = readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); mvpwm->blink_off_duration = @@ -703,8 +756,8 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm = mvchip->mvpwm; - writel_relaxed(mvpwm->blink_select, - mvebu_gpioreg_blink_counter_select(mvchip)); + regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, + mvpwm->blink_select); writel_relaxed(mvpwm->blink_on_duration, mvebu_pwmreg_blink_on_duration(mvpwm)); writel_relaxed(mvpwm->blink_off_duration, @@ -747,7 +800,8 @@ static int mvebu_pwm_probe(struct platform_device *pdev, set = U32_MAX; else return -EINVAL; - writel_relaxed(set, mvebu_gpioreg_blink_counter_select(mvchip)); + regmap_write(mvchip->regs, + GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set); mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); if (!mvpwm) @@ -790,14 +844,14 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; int i; - out = readl_relaxed(mvebu_gpioreg_out(mvchip)); - io_conf = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - blink = readl_relaxed(mvebu_gpioreg_blink(mvchip)); - in_pol = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - data_in = readl_relaxed(mvebu_gpioreg_data_in(mvchip)); - cause = readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)); - edg_msk = readl_relaxed(mvebu_gpioreg_edge_mask(mvchip)); - lvl_msk = readl_relaxed(mvebu_gpioreg_level_mask(mvchip)); + regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &io_conf); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &blink); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, &in_pol); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in); + cause = mvebu_gpio_read_edge_cause(mvchip); + edg_msk = mvebu_gpio_read_edge_mask(mvchip); + lvl_msk = mvebu_gpio_read_level_mask(mvchip); for (i = 0; i < chip->ngpio; i++) { const char *label; @@ -856,6 +910,10 @@ static const struct of_device_id mvebu_gpio_of_match[] = { .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION, }, { + .compatible = "marvell,armada-8k-gpio", + .data = (void *) MVEBU_GPIO_SOC_VARIANT_A8K, + }, + { /* sentinel */ }, }; @@ -865,36 +923,41 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); int i; - mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip)); - mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip)); - mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip)); - mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip)); + regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, + &mvchip->out_reg); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, + &mvchip->io_conf_reg); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + &mvchip->blink_en_reg); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, + &mvchip->in_pol_reg); switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - mvchip->edge_mask_regs[0] = - readl(mvchip->membase + GPIO_EDGE_MASK_OFF); - mvchip->level_mask_regs[0] = - readl(mvchip->membase + GPIO_LEVEL_MASK_OFF); + case MVEBU_GPIO_SOC_VARIANT_A8K: + regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset, + &mvchip->edge_mask_regs[0]); + regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset, + &mvchip->level_mask_regs[0]); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: for (i = 0; i < 2; i++) { - mvchip->edge_mask_regs[i] = - readl(mvchip->membase + - GPIO_EDGE_MASK_MV78200_OFF(i)); - mvchip->level_mask_regs[i] = - readl(mvchip->membase + - GPIO_LEVEL_MASK_MV78200_OFF(i)); + regmap_read(mvchip->regs, + GPIO_EDGE_MASK_MV78200_OFF(i), + &mvchip->edge_mask_regs[i]); + regmap_read(mvchip->regs, + GPIO_LEVEL_MASK_MV78200_OFF(i), + &mvchip->level_mask_regs[i]); } break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: for (i = 0; i < 4; i++) { - mvchip->edge_mask_regs[i] = - readl(mvchip->membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(i)); - mvchip->level_mask_regs[i] = - readl(mvchip->membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + regmap_read(mvchip->regs, + GPIO_EDGE_MASK_ARMADAXP_OFF(i), + &mvchip->edge_mask_regs[i]); + regmap_read(mvchip->regs, + GPIO_LEVEL_MASK_ARMADAXP_OFF(i), + &mvchip->level_mask_regs[i]); } break; default: @@ -912,35 +975,41 @@ static int mvebu_gpio_resume(struct platform_device *pdev) struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); int i; - writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip)); - writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip)); - writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip)); - writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip)); + regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, + mvchip->out_reg); + regmap_write(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, + mvchip->io_conf_reg); + regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + mvchip->blink_en_reg); + regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, + mvchip->in_pol_reg); switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - writel(mvchip->edge_mask_regs[0], - mvchip->membase + GPIO_EDGE_MASK_OFF); - writel(mvchip->level_mask_regs[0], - mvchip->membase + GPIO_LEVEL_MASK_OFF); + case MVEBU_GPIO_SOC_VARIANT_A8K: + regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset, + mvchip->edge_mask_regs[0]); + regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset, + mvchip->level_mask_regs[0]); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: for (i = 0; i < 2; i++) { - writel(mvchip->edge_mask_regs[i], - mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i)); - writel(mvchip->level_mask_regs[i], - mvchip->membase + - GPIO_LEVEL_MASK_MV78200_OFF(i)); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_MV78200_OFF(i), + mvchip->edge_mask_regs[i]); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_MV78200_OFF(i), + mvchip->level_mask_regs[i]); } break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: for (i = 0; i < 4; i++) { - writel(mvchip->edge_mask_regs[i], - mvchip->membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(i)); - writel(mvchip->level_mask_regs[i], - mvchip->membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_ARMADAXP_OFF(i), + mvchip->edge_mask_regs[i]); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_ARMADAXP_OFF(i), + mvchip->level_mask_regs[i]); } break; default: @@ -953,12 +1022,73 @@ static int mvebu_gpio_resume(struct platform_device *pdev) return 0; } +static const struct regmap_config mvebu_gpio_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static int mvebu_gpio_probe_raw(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip) +{ + struct resource *res; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvchip->regs)) + return PTR_ERR(mvchip->regs); + + /* + * For the legacy SoCs, the regmap directly maps to the GPIO + * registers, so no offset is needed. + */ + mvchip->offset = 0; + + /* + * The Armada XP has a second range of registers for the + * per-CPU registers + */ + if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvchip->percpu_regs = + devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvchip->percpu_regs)) + return PTR_ERR(mvchip->percpu_regs); + } + + return 0; +} + +static int mvebu_gpio_probe_syscon(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip) +{ + mvchip->regs = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(mvchip->regs)) + return PTR_ERR(mvchip->regs); + + if (of_property_read_u32(pdev->dev.of_node, "offset", &mvchip->offset)) + return -EINVAL; + + return 0; +} + static int mvebu_gpio_probe(struct platform_device *pdev) { struct mvebu_gpio_chip *mvchip; const struct of_device_id *match; struct device_node *np = pdev->dev.of_node; - struct resource *res; struct irq_chip_generic *gc; struct irq_chip_type *ct; unsigned int ngpios; @@ -1016,53 +1146,47 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.of_node = np; mvchip->chip.dbg_show = mvebu_gpio_dbg_show; - spin_lock_init(&mvchip->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mvchip->membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mvchip->membase)) - return PTR_ERR(mvchip->membase); + if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) + err = mvebu_gpio_probe_syscon(pdev, mvchip); + else + err = mvebu_gpio_probe_raw(pdev, mvchip); - /* - * The Armada XP has a second range of registers for the - * per-CPU registers - */ - if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev, - res); - if (IS_ERR(mvchip->percpu_membase)) - return PTR_ERR(mvchip->percpu_membase); - } + if (err) + return err; /* * Mask and clear GPIO interrupts. */ switch (soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); - writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF); - writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF); + case MVEBU_GPIO_SOC_VARIANT_A8K: + regmap_write(mvchip->regs, + GPIO_EDGE_CAUSE_OFF + mvchip->offset, 0); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_OFF + mvchip->offset, 0); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_OFF + mvchip->offset, 0); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: - writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); + regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); for (cpu = 0; cpu < 2; cpu++) { - writel_relaxed(0, mvchip->membase + - GPIO_EDGE_MASK_MV78200_OFF(cpu)); - writel_relaxed(0, mvchip->membase + - GPIO_LEVEL_MASK_MV78200_OFF(cpu)); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_MV78200_OFF(cpu), 0); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_MV78200_OFF(cpu), 0); } break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: - writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); - writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF); - writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF); + regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); + regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0); + regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0); for (cpu = 0; cpu < 4; cpu++) { - writel_relaxed(0, mvchip->percpu_membase + - GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu)); - writel_relaxed(0, mvchip->percpu_membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(cpu)); - writel_relaxed(0, mvchip->percpu_membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu)); + regmap_write(mvchip->percpu_regs, + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu), 0); + regmap_write(mvchip->percpu_regs, + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu), 0); + regmap_write(mvchip->percpu_regs, + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu), 0); } break; default: diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 8ddf9302ce3b..a4fd78b9c0e4 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -20,7 +20,7 @@ #include <linux/gpio.h> #include <linux/i2c.h> -#include <linux/i2c/pcf857x.h> +#include <linux/platform_data/pcf857x.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 71bc6da11337..f6600f8ada52 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -331,14 +331,18 @@ static irqreturn_t pch_gpio_handler(int irq, void *dev_id) return ret; } -static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, - unsigned int irq_start, unsigned int num) +static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip, + unsigned int irq_start, + unsigned int num) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base, handle_simple_irq); + if (!gc) + return -ENOMEM; + gc->private = chip; ct = gc->chip_types; @@ -349,6 +353,8 @@ static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST | IRQ_NOPROBE, 0); + + return 0; } static int pch_gpio_probe(struct pci_dev *pdev, @@ -425,7 +431,10 @@ static int pch_gpio_probe(struct pci_dev *pdev, goto err_request_irq; } - pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); + ret = pch_gpio_alloc_generic_chip(chip, irq_base, + gpio_pins[chip->ioh]); + if (ret) + goto err_request_irq; end: return 0; diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 31ad288846af..4a1536a050bc 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -344,6 +344,10 @@ static const struct gpio_rcar_info gpio_rcar_info_gen2 = { static const struct of_device_id gpio_rcar_of_table[] = { { + .compatible = "renesas,gpio-r8a7743", + /* RZ/G1 GPIO is identical to R-Car Gen2. */ + .data = &gpio_rcar_info_gen2, + }, { .compatible = "renesas,gpio-r8a7790", .data = &gpio_rcar_info_gen2, }, { diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c index 39df0620fa38..9e705162da8d 100644 --- a/drivers/gpio/gpio-sta2x11.c +++ b/drivers/gpio/gpio-sta2x11.c @@ -320,13 +320,16 @@ static irqreturn_t gsta_gpio_handler(int irq, void *dev_id) return ret; } -static void gsta_alloc_irq_chip(struct gsta_gpio *chip) +static int gsta_alloc_irq_chip(struct gsta_gpio *chip) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base, chip->reg_base, handle_simple_irq); + if (!gc) + return -ENOMEM; + gc->private = chip; ct = gc->chip_types; @@ -350,6 +353,8 @@ static void gsta_alloc_irq_chip(struct gsta_gpio *chip) } gc->irq_cnt = i - gc->irq_base; } + + return 0; } /* The platform device used here is instantiated by the MFD device */ @@ -400,7 +405,10 @@ static int gsta_probe(struct platform_device *dev) return err; } chip->irq_base = err; - gsta_alloc_irq_chip(chip); + + err = gsta_alloc_irq_chip(chip); + if (err) + return err; err = devm_request_irq(&dev->dev, pdev->irq, gsta_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 7b1bc20be209..85341eab795d 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -108,19 +108,14 @@ struct wcove_gpio { static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type) { unsigned int reg; - int bank; - if (gpio < BANK0_NR_PINS) - bank = 0; - else if (gpio < BANK0_NR_PINS + BANK1_NR_PINS) - bank = 1; - else - bank = 2; + if (gpio >= WCOVE_GPIO_NUM) + return -EOPNOTSUPP; if (reg_type == CTRL_IN) - reg = GPIO_IN_CTRL_BASE + bank; + reg = GPIO_IN_CTRL_BASE + gpio; else - reg = GPIO_OUT_CTRL_BASE + bank; + reg = GPIO_OUT_CTRL_BASE + gpio; return reg; } @@ -145,7 +140,10 @@ static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) { - unsigned int reg = to_reg(gpio, CTRL_IN); + int reg = to_reg(gpio, CTRL_IN); + + if (reg < 0) + return; regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt); } @@ -153,27 +151,36 @@ static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) static int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); + + if (reg < 0) + return 0; - return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_INPUT_SET); + return regmap_write(wg->regmap, reg, CTLO_INPUT_SET); } static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); - return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_OUTPUT_SET | value); + if (reg < 0) + return 0; + + return regmap_write(wg->regmap, reg, CTLO_OUTPUT_SET | value); } static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); unsigned int val; - int ret; + int ret, reg = to_reg(gpio, CTRL_OUT); + + if (reg < 0) + return 0; - ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &val); + ret = regmap_read(wg->regmap, reg, &val); if (ret) return ret; @@ -184,9 +191,12 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); unsigned int val; - int ret; + int ret, reg = to_reg(gpio, CTRL_IN); - ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &val); + if (reg < 0) + return 0; + + ret = regmap_read(wg->regmap, reg, &val); if (ret) return ret; @@ -197,25 +207,33 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); + + if (reg < 0) + return; if (value) - regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 1); + regmap_update_bits(wg->regmap, reg, 1, 1); else - regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 0); + regmap_update_bits(wg->regmap, reg, 1, 0); } static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, unsigned long config) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); + + if (reg < 0) + return 0; switch (pinconf_to_config_param(config)) { case PIN_CONFIG_DRIVE_OPEN_DRAIN: - return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_DRV_MASK, CTLO_DRV_OD); + return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, + CTLO_DRV_OD); case PIN_CONFIG_DRIVE_PUSH_PULL: - return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_DRV_MASK, CTLO_DRV_CMOS); + return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, + CTLO_DRV_CMOS); default: break; } @@ -228,6 +246,9 @@ static int wcove_irq_type(struct irq_data *data, unsigned int type) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct wcove_gpio *wg = gpiochip_get_data(chip); + if (data->hwirq >= WCOVE_GPIO_NUM) + return 0; + switch (type) { case IRQ_TYPE_NONE: wg->intcnt = CTLI_INTCNT_DIS; @@ -278,6 +299,9 @@ static void wcove_irq_unmask(struct irq_data *data) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct wcove_gpio *wg = gpiochip_get_data(chip); + if (data->hwirq >= WCOVE_GPIO_NUM) + return; + wg->set_irq_mask = false; wg->update |= UPDATE_IRQ_MASK; } @@ -287,6 +311,9 @@ static void wcove_irq_mask(struct irq_data *data) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct wcove_gpio *wg = gpiochip_get_data(chip); + if (data->hwirq >= WCOVE_GPIO_NUM) + return; + wg->set_irq_mask = true; wg->update |= UPDATE_IRQ_MASK; } @@ -401,7 +428,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) if (!wg) return -ENOMEM; - wg->regmap_irq_chip = pmic->irq_chip_data_level2; + wg->regmap_irq_chip = pmic->irq_chip_data; platform_set_drvdata(pdev, wg); @@ -449,6 +476,18 @@ static int wcove_gpio_probe(struct platform_device *pdev) gpiochip_set_nested_irqchip(&wg->chip, &wcove_irqchip, virq); + /* Enable GPIO0 interrupts */ + ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK, + 0x00); + if (ret) + return ret; + + /* Enable GPIO1 interrupts */ + ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK, + 0x00); + if (ret) + return ret; + return 0; } diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c new file mode 100644 index 000000000000..0230e4b7a2fb --- /dev/null +++ b/drivers/gpio/gpio-xra1403.c @@ -0,0 +1,237 @@ +/* + * GPIO driver for EXAR XRA1403 16-bit GPIO expander + * + * Copyright (c) 2017, General Electric Company + * + * 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/seq_file.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +/* XRA1403 registers */ +#define XRA_GSR 0x00 /* GPIO State */ +#define XRA_OCR 0x02 /* Output Control */ +#define XRA_PIR 0x04 /* Input Polarity Inversion */ +#define XRA_GCR 0x06 /* GPIO Configuration */ +#define XRA_PUR 0x08 /* Input Internal Pull-up Resistor Enable/Disable */ +#define XRA_IER 0x0A /* Input Interrupt Enable */ +#define XRA_TSCR 0x0C /* Output Three-State Control */ +#define XRA_ISR 0x0E /* Input Interrupt Status */ +#define XRA_REIR 0x10 /* Input Rising Edge Interrupt Enable */ +#define XRA_FEIR 0x12 /* Input Falling Edge Interrupt Enable */ +#define XRA_IFR 0x14 /* Input Filter Enable/Disable */ + +struct xra1403 { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static const struct regmap_config xra1403_regmap_cfg = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + + .max_register = XRA_IFR | 0x01, +}; + +static unsigned int to_reg(unsigned int reg, unsigned int offset) +{ + return reg + (offset > 7); +} + +static int xra1403_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct xra1403 *xra = gpiochip_get_data(chip); + + return regmap_update_bits(xra->regmap, to_reg(XRA_GCR, offset), + BIT(offset % 8), BIT(offset % 8)); +} + +static int xra1403_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + int ret; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_update_bits(xra->regmap, to_reg(XRA_GCR, offset), + BIT(offset % 8), 0); + if (ret) + return ret; + + ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), + BIT(offset % 8), value ? BIT(offset % 8) : 0); + + return ret; +} + +static int xra1403_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + unsigned int val; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_read(xra->regmap, to_reg(XRA_GCR, offset), &val); + if (ret) + return ret; + + return !!(val & BIT(offset % 8)); +} + +static int xra1403_get(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + unsigned int val; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_read(xra->regmap, to_reg(XRA_GSR, offset), &val); + if (ret) + return ret; + + return !!(val & BIT(offset % 8)); +} + +static void xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + int ret; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), + BIT(offset % 8), value ? BIT(offset % 8) : 0); + if (ret) + dev_err(chip->parent, "Failed to set pin: %d, ret: %d\n", + offset, ret); +} + +#ifdef CONFIG_DEBUG_FS +static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + int reg; + struct xra1403 *xra = gpiochip_get_data(chip); + int value[xra1403_regmap_cfg.max_register]; + int i; + unsigned int gcr; + unsigned int gsr; + + seq_puts(s, "xra reg:"); + for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++) + seq_printf(s, " %2.2x", reg); + seq_puts(s, "\n value:"); + for (reg = 0; reg < xra1403_regmap_cfg.max_register; reg++) { + regmap_read(xra->regmap, reg, &value[reg]); + seq_printf(s, " %2.2x", value[reg]); + } + seq_puts(s, "\n"); + + gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR]; + gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR]; + for (i = 0; i < chip->ngpio; i++) { + const char *label = gpiochip_is_requested(chip, i); + + if (!label) + continue; + + seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", + chip->base + i, label, + (gcr & BIT(i)) ? "in" : "out", + (gsr & BIT(i)) ? "hi" : "lo"); + } +} +#else +#define xra1403_dbg_show NULL +#endif + +static int xra1403_probe(struct spi_device *spi) +{ + struct xra1403 *xra; + struct gpio_desc *reset_gpio; + int ret; + + xra = devm_kzalloc(&spi->dev, sizeof(*xra), GFP_KERNEL); + if (!xra) + return -ENOMEM; + + /* bring the chip out of reset if reset pin is provided*/ + reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + dev_warn(&spi->dev, "Could not get reset-gpios\n"); + + xra->chip.direction_input = xra1403_direction_input; + xra->chip.direction_output = xra1403_direction_output; + xra->chip.get_direction = xra1403_get_direction; + xra->chip.get = xra1403_get; + xra->chip.set = xra1403_set; + + xra->chip.dbg_show = xra1403_dbg_show; + + xra->chip.ngpio = 16; + xra->chip.label = "xra1403"; + + xra->chip.base = -1; + xra->chip.can_sleep = true; + xra->chip.parent = &spi->dev; + xra->chip.owner = THIS_MODULE; + + xra->regmap = devm_regmap_init_spi(spi, &xra1403_regmap_cfg); + if (IS_ERR(xra->regmap)) { + ret = PTR_ERR(xra->regmap); + dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + ret = devm_gpiochip_add_data(&spi->dev, &xra->chip, xra); + if (ret < 0) { + dev_err(&spi->dev, "Unable to register gpiochip\n"); + return ret; + } + + spi_set_drvdata(spi, xra); + + return 0; +} + +static const struct spi_device_id xra1403_ids[] = { + { "xra1403" }, + {}, +}; +MODULE_DEVICE_TABLE(spi, xra1403_ids); + +static const struct of_device_id xra1403_spi_of_match[] = { + { .compatible = "exar,xra1403" }, + {}, +}; +MODULE_DEVICE_TABLE(of, xra1403_spi_of_match); + +static struct spi_driver xra1403_driver = { + .probe = xra1403_probe, + .id_table = xra1403_ids, + .driver = { + .name = "xra1403", + .of_match_table = of_match_ptr(xra1403_spi_of_match), + }, +}; + +module_spi_driver(xra1403_driver); + +MODULE_AUTHOR("Nandor Han <nandor.han@ge.com>"); +MODULE_AUTHOR("Semi Malinen <semi.malinen@ge.com>"); +MODULE_DESCRIPTION("GPIO expander driver for EXAR XRA1403"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 6b4d10d6e10f..df0851464006 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -96,8 +96,8 @@ /* GPIO upper 16 bit mask */ #define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000 -/* For GPIO quirks */ -#define ZYNQ_GPIO_QUIRK_FOO BIT(0) +/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */ +#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0) /** * struct zynq_gpio - gpio device private data structure @@ -136,6 +136,17 @@ static struct irq_chip zynq_gpio_level_irqchip; static struct irq_chip zynq_gpio_edge_irqchip; /** + * zynq_gpio_is_zynq - test if HW is zynq or zynqmp + * @gpio: Pointer to driver data struct + * + * Return: 0 if zynqmp, 1 if zynq. + */ +static int zynq_gpio_is_zynq(struct zynq_gpio *gpio) +{ + return !!(gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_IS_ZYNQ); +} + +/** * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank * for a given pin in the GPIO device * @pin_num: gpio pin number within the device @@ -242,18 +253,16 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) { u32 reg; - bool is_zynq_gpio; unsigned int bank_num, bank_pin_num; struct zynq_gpio *gpio = gpiochip_get_data(chip); - is_zynq_gpio = gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_FOO; zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* * On zynq bank 0 pins 7 and 8 are special and cannot be used * as inputs. */ - if (is_zynq_gpio && bank_num == 0 && + if (zynq_gpio_is_zynq(gpio) && bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8)) return -EINVAL; @@ -637,7 +646,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = { static const struct zynq_platform_data zynq_gpio_def = { .label = "zynq_gpio", - .quirks = ZYNQ_GPIO_QUIRK_FOO, + .quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ, .ngpio = ZYNQ_GPIO_NR_GPIOS, .max_bank = ZYNQ_GPIO_MAX_BANK, .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(), @@ -651,9 +660,8 @@ static const struct zynq_platform_data zynq_gpio_def = { }; static const struct of_device_id zynq_gpio_of_match[] = { - { .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def }, - { .compatible = "xlnx,zynqmp-gpio-1.0", - .data = (void *)&zynqmp_gpio_def }, + { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, + { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 2185232da823..c9b42dd12dfa 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -165,6 +165,23 @@ static void acpi_gpio_chip_dh(acpi_handle handle, void *data) /* The address of this function is used as a key. */ } +bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio) +{ + struct acpi_resource_gpio *gpio; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return false; + + gpio = &ares->data.gpio; + if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) + return false; + + *agpio = gpio; + return true; +} +EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); + static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, void *context) { @@ -178,11 +195,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, unsigned long irqflags; int ret, pin, irq; - if (ares->type != ACPI_RESOURCE_TYPE_GPIO) - return AE_OK; - - agpio = &ares->data.gpio; - if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) + if (!acpi_gpio_get_irq_resource(ares, &agpio)) return AE_OK; handle = ACPI_HANDLE(chip->parent); @@ -201,7 +214,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, handler = acpi_gpio_irq_handler_evt; } if (!handler) - return AE_BAD_PARAMETER; + return AE_OK; pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (pin < 0) @@ -423,6 +436,59 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, return false; } +static enum gpiod_flags +acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) +{ + bool pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP; + + switch (agpio->io_restriction) { + case ACPI_IO_RESTRICT_INPUT: + return GPIOD_IN; + case ACPI_IO_RESTRICT_OUTPUT: + /* + * ACPI GPIO resources don't contain an initial value for the + * GPIO. Therefore we deduce that value from the pull field + * instead. If the pin is pulled up we assume default to be + * high, otherwise low. + */ + return pull_up ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + default: + /* + * Assume that the BIOS has configured the direction and pull + * accordingly. + */ + return GPIOD_ASIS; + } +} + +int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + int ret = 0; + + /* + * Check if the BIOS has IoRestriction with explicitly set direction + * and update @flags accordingly. Otherwise use whatever caller asked + * for. + */ + if (update & GPIOD_FLAGS_BIT_DIR_SET) { + enum gpiod_flags diff = *flags ^ update; + + /* + * Check if caller supplied incompatible GPIO initialization + * flags. + * + * Return %-EINVAL to notify that firmware has different + * settings and we are going to use them. + */ + if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || + ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) + ret = -EINVAL; + *flags = update; + } + return ret; +} + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; @@ -460,8 +526,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH */ if (lookup->info.gpioint) { + lookup->info.flags = GPIOD_IN; lookup->info.polarity = agpio->polarity; lookup->info.triggering = agpio->triggering; + } else { + lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); } } @@ -588,18 +657,19 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, - enum gpiod_flags flags, + enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags) { struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; + int err; int i; /* Try first from _DSD */ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id && strcmp(con_id, "gpios")) { + if (con_id) { snprintf(propname, sizeof(propname), "%s-%s", con_id, gpio_suffixes[i]); } else { @@ -622,17 +692,21 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, 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.gpioint && + (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { + dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); + return ERR_PTR(-ENOENT); } if (info.polarity == GPIO_ACTIVE_LOW) *lookupflags |= GPIO_ACTIVE_LOW; + err = acpi_gpio_update_gpiod_flags(dflags, info.flags); + if (err) + dev_dbg(dev, "Override GPIO initialization flags\n"); + return desc; } @@ -686,12 +760,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, * used to translate from the GPIO offset in the resource to the Linux IRQ * number. * + * The function is idempotent, though each time it runs it will configure GPIO + * pin direction according to the flags in GpioInt resource. + * * Return: Linux IRQ number (>%0) on success, negative errno on failure. */ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) { int idx, i; unsigned int irq_flags; + int ret; for (i = 0, idx = 0; idx <= index; i++) { struct acpi_gpio_info info; @@ -704,6 +782,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) return PTR_ERR(desc); if (info.gpioint && idx++ == index) { + char label[32]; int irq; if (IS_ERR(desc)) @@ -713,6 +792,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) if (irq < 0) return irq; + snprintf(label, sizeof(label), "GpioInt() %d", index); + ret = gpiod_configure_flags(desc, label, 0, info.flags); + if (ret < 0) + return ret; + irq_flags = acpi_dev_get_irq_type(info.triggering, info.polarity); @@ -740,7 +824,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct acpi_resource *ares; int pin_index = (int)address; acpi_status status; - bool pull_up; int length; int i; @@ -755,7 +838,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, } agpio = &ares->data.gpio; - pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP; if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT && function == ACPI_WRITE)) { @@ -806,35 +888,23 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, } if (!found) { - desc = gpiochip_request_own_desc(chip, pin, - "ACPI:OpRegion"); + enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio); + const char *label = "ACPI:OpRegion"; + int err; + + desc = gpiochip_request_own_desc(chip, pin, label); if (IS_ERR(desc)) { status = AE_ERROR; mutex_unlock(&achip->conn_lock); goto out; } - switch (agpio->io_restriction) { - case ACPI_IO_RESTRICT_INPUT: - gpiod_direction_input(desc); - break; - case ACPI_IO_RESTRICT_OUTPUT: - /* - * ACPI GPIO resources don't contain an - * initial value for the GPIO. Therefore we - * deduce that value from the pull field - * instead. If the pin is pulled up we - * assume default to be high, otherwise - * low. - */ - gpiod_direction_output(desc, pull_up); - break; - default: - /* - * Assume that the BIOS has configured the - * direction and pull accordingly. - */ - break; + err = gpiod_configure_flags(desc, label, 0, flags); + if (err < 0) { + status = AE_NOT_CONFIGURED; + gpiochip_free_own_desc(desc); + mutex_unlock(&achip->conn_lock); + goto out; } conn = kzalloc(sizeof(*conn), GFP_KERNEL); @@ -1089,7 +1159,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id) /* Try first from _DSD */ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id && strcmp(con_id, "gpios")) + if (con_id) snprintf(propname, sizeof(propname), "%s-%s", con_id, gpio_suffixes[i]); else @@ -1119,6 +1189,9 @@ int acpi_gpio_count(struct device *dev, const char *con_id) struct list_head resource_list; unsigned int crs_count = 0; + if (!acpi_can_fallback_to_crs(adev, con_id)) + return count; + INIT_LIST_HEAD(&resource_list); acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio_count, &crs_count); @@ -1129,45 +1202,11 @@ int acpi_gpio_count(struct device *dev, const char *con_id) return count ? count : -ENOENT; } -struct acpi_crs_lookup { - struct list_head node; - struct acpi_device *adev; - const char *con_id; -}; - -static DEFINE_MUTEX(acpi_crs_lookup_lock); -static LIST_HEAD(acpi_crs_lookup_list); - bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) { - struct acpi_crs_lookup *l, *lookup = NULL; - /* Never allow fallback if the device has properties */ if (adev->data.properties || adev->driver_gpios) return false; - mutex_lock(&acpi_crs_lookup_lock); - - list_for_each_entry(l, &acpi_crs_lookup_list, node) { - if (l->adev == adev) { - lookup = l; - break; - } - } - - if (!lookup) { - lookup = kmalloc(sizeof(*lookup), GFP_KERNEL); - if (lookup) { - lookup->adev = adev; - lookup->con_id = kstrdup(con_id, GFP_KERNEL); - list_add_tail(&lookup->node, &acpi_crs_lookup_list); - } - } - - mutex_unlock(&acpi_crs_lookup_lock); - - return lookup && - ((!lookup->con_id && !con_id) || - (lookup->con_id && con_id && - strcmp(lookup->con_id, con_id) == 0)); + return con_id == NULL; } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index b13b7c7c335f..54ce8dc58ad0 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -153,6 +153,9 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_OPEN_SOURCE; } + if (of_flags & OF_GPIO_SLEEP_MAY_LOOSE_VALUE) + *flags |= GPIO_SLEEP_MAY_LOOSE_VALUE; + return desc; } @@ -236,7 +239,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, * * This is only used by of_gpiochip_add to request/set GPIO initial * configuration. - * It retures error if it fails otherwise 0 on success. + * It returns error if it fails otherwise 0 on success. */ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) { diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 4b44dd97c07f..16fe9742597b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -479,6 +479,7 @@ done: pr_debug("%s: status %d\n", __func__, status); return status ? : len; } +static CLASS_ATTR_WO(export); static ssize_t unexport_store(struct class *class, struct class_attribute *attr, @@ -514,18 +515,20 @@ done: pr_debug("%s: status %d\n", __func__, status); return status ? : len; } +static CLASS_ATTR_WO(unexport); -static struct class_attribute gpio_class_attrs[] = { - __ATTR(export, 0200, NULL, export_store), - __ATTR(unexport, 0200, NULL, unexport_store), - __ATTR_NULL, +static struct attribute *gpio_class_attrs[] = { + &class_attr_export.attr, + &class_attr_unexport.attr, + NULL, }; +ATTRIBUTE_GROUPS(gpio_class); static struct class gpio_class = { .name = "gpio", .owner = THIS_MODULE, - .class_attrs = gpio_class_attrs, + .class_groups = gpio_class_groups, }; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5db44139cef8..9568708a550b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,4 +1,4 @@ -#include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -708,7 +708,8 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) ge.timestamp = ktime_get_real_ns(); - if (le->eflags & GPIOEVENT_REQUEST_BOTH_EDGES) { + if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE + && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { int level = gpiod_get_value_cansleep(le->desc); if (level) @@ -1471,8 +1472,6 @@ static struct gpio_chip *find_chip_by_name(const char *name) static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) { - int i; - if (!gpiochip->irq_need_valid_mask) return 0; @@ -1482,8 +1481,7 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) return -ENOMEM; /* Assume by default all GPIOs are valid */ - for (i = 0; i < gpiochip->ngpio; i++) - set_bit(i, gpiochip->irq_valid_mask); + bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); return 0; } @@ -2869,6 +2867,16 @@ bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return !test_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, + &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); + /** * gpiod_get_raw_value_cansleep() - return a gpio's raw value * @desc: gpio whose value will be returned @@ -3008,6 +3016,7 @@ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) mutex_unlock(&gpio_lookup_lock); } +EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); /** * gpiod_remove_lookup_table() - unregister GPIO device consumers @@ -3021,6 +3030,7 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) mutex_unlock(&gpio_lookup_lock); } +EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) { @@ -3212,7 +3222,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional); * requested function and/or index, or another IS_ERR() code if an error * occurred while trying to acquire the GPIO. */ -static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags) { int status; @@ -3223,6 +3233,8 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIO_SLEEP_MAY_LOOSE_VALUE) + set_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, &desc->flags); /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { @@ -3272,7 +3284,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, flags, &lookupflags); + desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags); } } @@ -3353,8 +3365,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct acpi_gpio_info info; desc = acpi_node_get_gpiod(fwnode, propname, index, &info); - if (!IS_ERR(desc)) + if (!IS_ERR(desc)) { active_low = info.polarity == GPIO_ACTIVE_LOW; + ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags); + if (ret) + pr_debug("Override GPIO initialization flags\n"); + } } if (IS_ERR(desc)) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 2495b7ee1b42..a8be286eff86 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -75,11 +75,13 @@ struct gpio_device { /** * struct acpi_gpio_info - ACPI GPIO specific information + * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI */ struct acpi_gpio_info { + enum gpiod_flags flags; bool gpioint; int polarity; int triggering; @@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip); void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); +int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, + enum gpiod_flags update); + struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, - enum gpiod_flags flags, + enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags); struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, int index, @@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } +static inline int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + return 0; +} + static inline struct gpio_desc * acpi_find_gpio(struct device *dev, const char *con_id, - unsigned int idx, enum gpiod_flags flags, + unsigned int idx, enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags) { return ERR_PTR(-ENOENT); @@ -190,6 +201,7 @@ struct gpio_desc { #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ +#define FLAG_SLEEP_MAY_LOOSE_VALUE 12 /* GPIO may loose value in sleep */ /* Connection label */ const char *label; @@ -199,6 +211,8 @@ struct gpio_desc { int gpiod_request(struct gpio_desc *desc, const char *label); void gpiod_free(struct gpio_desc *desc); +int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, + unsigned long lflags, enum gpiod_flags dflags); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags); |