diff options
author | Linus Torvalds | 2019-03-08 10:09:53 -0800 |
---|---|---|
committer | Linus Torvalds | 2019-03-08 10:09:53 -0800 |
commit | 3601fe43e8164f67a8de3de8e988bfcb3a94af46 (patch) | |
tree | a9b9f0a9890e97a985c2722600126e93fd9d27ab /drivers | |
parent | cf2e8c544cd3b33e9e403b7b72404c221bf888d1 (diff) | |
parent | d01849f7deba81f4959fd9e51bf20dbf46987d1c (diff) |
Merge tag 'gpio-v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v5.1 cycle:
Core changes:
- The big change this time around is the irqchip handling in the
qualcomm pin controllers, closely coupled with the gpiochip. This
rework, in a classic fall-between-the-chairs fashion has been
sidestepped for too long.
The Qualcomm IRQchips using the SPMI and SSBI transport mechanisms
have been rewritten to use hierarchical irqchip. This creates the
base from which I intend to gradually pull support for hierarchical
irqchips into the gpiolib irqchip helpers to cut down on duplicate
code.
We have too many hacks in the kernel because people have been
working around the missing hierarchical irqchip for years, and once
it was there, noone understood it for a while. We are now slowly
adapting to using it.
This is why this pull requests include changes to MFD, SPMI,
IRQchip core and some ARM Device Trees pertaining to the Qualcomm
chip family. Since Qualcomm have so many chips and such large
deployments it is paramount that this platform gets this right, and
now it (hopefully) does.
- Core support for pull-up and pull-down configuration, also from the
device tree. When a simple GPIO chip supports an "off or on" pull-up
or pull-down resistor, we provide a way to set this up using
machine descriptors or device tree.
If more elaborate control of pull up/down (such as resistance shunt
setting) is required, drivers should be phased over to use pin
control. We do not yet provide a userspace ABI for this pull
up-down setting but I suspect the makers are going to ask for it
soon enough. PCA953x is the first user of this new API.
- The GPIO mockup driver has been revamped after some discussion
improving the IRQ simulator in the process.
The idea is to make it possible to use the mockup for both testing
and virtual prototyping, e.g. when you do not yet have a GPIO
expander to play with but really want to get something to develop
code around before hardware is available. It's neat. The blackbox
testing usecase is currently making its way into kernelci.
- ACPI GPIO core preserves non direction flags when updating flags.
- A new device core helper for devm_platform_ioremap_resource() is
funneled through the GPIO tree with Greg's ACK.
New drivers:
- TQ-Systems QTMX86 GPIO controllers (using port-mapped I/O)
- Gateworks PLD GPIO driver (vaccumed up from OpenWrt)
- AMD G-Series PCH (Platform Controller Hub) GPIO driver.
- Fintek F81804 & F81966 subvariants.
- PCA953x now supports NXP PCAL6416.
Driver improvements:
- IRQ support on the Nintendo Wii (Hollywood) GPIO.
- get_direction() support for the MVEBU driver.
- Set the right output level on SAMA5D2.
- Drop the unused irq trigger setting on the Spreadtrum driver.
- Wakeup support for PCA953x.
- A slew of cleanups in the various Intel drivers"
* tag 'gpio-v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (110 commits)
gpio: gpio-omap: fix level interrupt idling
gpio: amd-fch: Set proper output level for direction_output
x86: apuv2: remove unused variable
gpio: pca953x: Use PCA_LATCH_INT
platform/x86: fix PCENGINES_APU2 Kconfig warning
gpio: pca953x: Fix dereference of irq data in shutdown
gpio: amd-fch: Fix type error found by sparse
gpio: amd-fch: Drop const from resource
gpio: mxc: add check to return defer probe if clock tree NOT ready
gpio: ftgpio: Register per-instance irqchip
gpio: ixp4xx: Add DT bindings
x86: pcengines apuv2 gpio/leds/keys platform driver
gpio: AMD G-Series PCH gpio driver
drivers: depend on HAS_IOMEM for devm_platform_ioremap_resource()
gpio: tqmx86: Set proper output level for direction_output
gpio: sprd: Change to use SoC compatible string
gpio: sprd: Use SoC compatible string instead of wildcard string
gpio: of: Handle both enable-gpio{,s}
gpio: of: Restrict enable-gpio quirk to regulator-gpio
gpio: davinci: use devm_platform_ioremap_resource()
...
Diffstat (limited to 'drivers')
44 files changed, 2191 insertions, 514 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 4e45ac21d672..dab0a5abc391 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -80,6 +80,26 @@ struct resource *platform_get_resource(struct platform_device *dev, EXPORT_SYMBOL_GPL(platform_get_resource); /** + * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform + * device + * + * @pdev: platform device to use both for memory resource lookup as well as + * resource managemend + * @index: resource index + */ +#ifdef CONFIG_HAS_IOMEM +void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, + unsigned int index) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, index); + return devm_ioremap_resource(&pdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); +#endif /* CONFIG_HAS_IOMEM */ + +/** * platform_get_irq - get an IRQ for a device * @dev: platform device * @num: IRQ number index diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b5a2845347ec..3f50526a771f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -258,6 +258,7 @@ config GPIO_HLWD tristate "Nintendo Wii (Hollywood) GPIO" depends on OF_GPIO select GPIO_GENERIC + select GPIOLIB_IRQCHIP help Select this to support the GPIO controller of the Nintendo Wii. @@ -654,6 +655,15 @@ config GPIO_LOONGSON1 help Say Y or M here to support GPIO on Loongson1 SoCs. +config GPIO_AMD_FCH + tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)" + help + This option enables driver for GPIO on AMDs Fusion Controller Hub, + as found on G-series SOCs (eg. GX-412TC) + + Note: This driver doesn't registers itself automatically, as it + needs to be provided with platform specific configuration. + (See eg. CONFIG_PCENGINES_APU2.) endmenu menu "Port-mapped I/O GPIO drivers" @@ -830,6 +840,13 @@ config GPIO_ADNP enough to represent all pins, but the driver will assume a register layout for 64 pins (8 registers). +config GPIO_GW_PLD + tristate "Gateworks PLD GPIO Expander" + depends on OF_GPIO + help + Say yes here to provide access to the Gateworks I2C PLD GPIO + Expander. This is used at least on the Cambria GW2358-4. + config GPIO_MAX7300 tristate "Maxim MAX7300 GPIO expander" select GPIO_MAX730X @@ -1190,6 +1207,13 @@ config GPIO_TPS68470 of the TPS68470 must be available before dependent drivers are loaded. +config GPIO_TQMX86 + tristate "TQ-Systems QTMX86 GPIO" + depends on MFD_TQMX86 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + This driver supports GPIO on the TQMX86 IO controller. + config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 37628f8dbf70..54d55274b93a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o +obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o @@ -55,6 +56,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o +obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o @@ -135,6 +137,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o +obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index cc33d8986ad3..c4a5b499f53e 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -15,6 +15,7 @@ #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/platform_data/adp5588.h> @@ -33,16 +34,13 @@ struct adp5588_gpio { struct mutex lock; /* protect cached dir, dat_out */ /* protect serialized access to the interrupt controller bus */ struct mutex irq_lock; - unsigned gpio_start; - unsigned irq_base; uint8_t dat_out[3]; uint8_t dir[3]; - uint8_t int_lvl[3]; + uint8_t int_lvl_low[3]; + uint8_t int_lvl_high[3]; uint8_t int_en[3]; uint8_t irq_mask[3]; - uint8_t irq_stat[3]; uint8_t int_input_en[3]; - uint8_t int_lvl_cached[3]; }; static int adp5588_gpio_read(struct i2c_client *client, u8 reg) @@ -148,16 +146,11 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, } #ifdef CONFIG_GPIO_ADP5588_IRQ -static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) -{ - struct adp5588_gpio *dev = gpiochip_get_data(chip); - - return dev->irq_base + off; -} static void adp5588_irq_bus_lock(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); mutex_lock(&dev->irq_lock); } @@ -172,7 +165,8 @@ static void adp5588_irq_bus_lock(struct irq_data *d) static void adp5588_irq_bus_sync_unlock(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); int i; for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { @@ -185,15 +179,9 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d) mutex_unlock(&dev->lock); } - if (dev->int_lvl_cached[i] != dev->int_lvl[i]) { - dev->int_lvl_cached[i] = dev->int_lvl[i]; - adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + i, - dev->int_lvl[i]); - } - if (dev->int_en[i] ^ dev->irq_mask[i]) { dev->int_en[i] = dev->irq_mask[i]; - adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, + adp5588_gpio_write(dev->client, GPI_EM1 + i, dev->int_en[i]); } } @@ -203,41 +191,38 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d) static void adp5588_irq_mask(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); - dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); + dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq); } static void adp5588_irq_unmask(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); - dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); + dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq); } static int adp5588_irq_set_type(struct irq_data *d, unsigned int type) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - uint16_t gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); + uint16_t gpio = d->hwirq; unsigned bank, bit; - if ((type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", - d->irq, type); - return -EINVAL; - } - bank = ADP5588_BANK(gpio); bit = ADP5588_BIT(gpio); - if (type & IRQ_TYPE_LEVEL_HIGH) - dev->int_lvl[bank] |= bit; - else if (type & IRQ_TYPE_LEVEL_LOW) - dev->int_lvl[bank] &= ~bit; - else - return -EINVAL; + dev->int_lvl_low[bank] &= ~bit; + dev->int_lvl_high[bank] &= ~bit; + + if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH) + dev->int_lvl_high[bank] |= bit; + + if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW) + dev->int_lvl_low[bank] |= bit; dev->int_input_en[bank] |= bit; @@ -253,40 +238,32 @@ static struct irq_chip adp5588_irq_chip = { .irq_set_type = adp5588_irq_set_type, }; -static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) -{ - int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); - - if (ret < 0) - dev_err(&client->dev, "Read INT_STAT Error\n"); - - return ret; -} - static irqreturn_t adp5588_irq_handler(int irq, void *devid) { struct adp5588_gpio *dev = devid; - unsigned status, bank, bit, pending; - int ret; - status = adp5588_gpio_read(dev->client, INT_STAT); - - if (status & ADP5588_GPI_INT) { - ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); - if (ret < 0) - memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); - - for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); - bank++, bit = 0) { - pending = dev->irq_stat[bank] & dev->irq_mask[bank]; - - while (pending) { - if (pending & (1 << bit)) { - handle_nested_irq(dev->irq_base + - (bank << 3) + bit); - pending &= ~(1 << bit); - - } - bit++; + int status = adp5588_gpio_read(dev->client, INT_STAT); + + if (status & ADP5588_KE_INT) { + int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT); + + if (ev_cnt > 0) { + int i; + + for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) { + int key = adp5588_gpio_read(dev->client, + Key_EVENTA + i); + /* GPIN events begin at 97, + * bit 7 indicates logic level + */ + int gpio = (key & 0x7f) - 97; + int lvl = key & (1 << 7); + int bank = ADP5588_BANK(gpio); + int bit = ADP5588_BIT(gpio); + + if ((lvl && dev->int_lvl_high[bank] & bit) || + (!lvl && dev->int_lvl_low[bank] & bit)) + handle_nested_irq(irq_find_mapping( + dev->gpio_chip.irq.domain, gpio)); } } } @@ -299,53 +276,42 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) static int adp5588_irq_setup(struct adp5588_gpio *dev) { struct i2c_client *client = dev->client; + int ret; struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); - unsigned gpio; - int ret; + int irq_base = pdata ? pdata->irq_base : 0; adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ - adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ - dev->irq_base = pdata->irq_base; mutex_init(&dev->irq_lock); - for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { - int irq = gpio + dev->irq_base; - irq_set_chip_data(irq, dev); - irq_set_chip_and_handler(irq, &adp5588_irq_chip, - handle_level_irq); - irq_set_nested_thread(irq, 1); - irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE); - } - - ret = request_threaded_irq(client->irq, - NULL, - adp5588_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, adp5588_irq_handler, IRQF_ONESHOT + | IRQF_TRIGGER_FALLING | IRQF_SHARED, + dev_name(&client->dev), dev); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", client->irq); - goto out; + return ret; } + ret = gpiochip_irqchip_add_nested(&dev->gpio_chip, + &adp5588_irq_chip, irq_base, + handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&client->dev, + "could not connect irqchip to gpiochip\n"); + return ret; + } + gpiochip_set_nested_irqchip(&dev->gpio_chip, + &adp5588_irq_chip, + client->irq); - dev->gpio_chip.to_irq = adp5588_gpio_to_irq; adp5588_gpio_write(client, CFG, - ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); return 0; - -out: - dev->irq_base = 0; - return ret; -} - -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ - if (dev->irq_base) - free_irq(dev->client->irq, dev); } #else @@ -357,24 +323,16 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) return 0; } -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ -} #endif /* CONFIG_GPIO_ADP5588_IRQ */ -static int adp5588_gpio_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adp5588_gpio_probe(struct i2c_client *client) { struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); struct adp5588_gpio *dev; struct gpio_chip *gc; int ret, i, revid; - - if (!pdata) { - dev_err(&client->dev, "missing platform data\n"); - return -ENODEV; - } + unsigned int pullup_dis_mask = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -394,18 +352,24 @@ static int adp5588_gpio_probe(struct i2c_client *client, gc->get = adp5588_gpio_get_value; gc->set = adp5588_gpio_set_value; gc->can_sleep = true; + gc->base = -1; + gc->parent = &client->dev; + + if (pdata) { + gc->base = pdata->gpio_start; + gc->names = pdata->names; + pullup_dis_mask = pdata->pullup_dis_mask; + } - gc->base = pdata->gpio_start; gc->ngpio = ADP5588_MAXGPIO; gc->label = client->name; gc->owner = THIS_MODULE; - gc->names = pdata->names; mutex_init(&dev->lock); ret = adp5588_gpio_read(dev->client, DEV_ID); if (ret < 0) - goto err; + return ret; revid = ret & ADP5588_DEVICE_ID_MASK; @@ -414,30 +378,27 @@ static int adp5588_gpio_probe(struct i2c_client *client, dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, - (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); + (pullup_dis_mask >> (8 * i)) & 0xFF); ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); if (ret) - goto err; + return ret; } - if (pdata->irq_base) { + if (client->irq) { if (WA_DELAYED_READOUT_REVID(revid)) { dev_warn(&client->dev, "GPIO int not supported\n"); } else { ret = adp5588_irq_setup(dev); if (ret) - goto err; + return ret; } } ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev); if (ret) - goto err_irq; + return ret; - dev_info(&client->dev, "IRQ Base: %d Rev.: %d\n", - pdata->irq_base, revid); - - if (pdata->setup) { + if (pdata && pdata->setup) { ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); if (ret < 0) dev_warn(&client->dev, "setup failed, %d\n", ret); @@ -446,11 +407,6 @@ static int adp5588_gpio_probe(struct i2c_client *client, i2c_set_clientdata(client, dev); return 0; - -err_irq: - adp5588_irq_teardown(dev); -err: - return ret; } static int adp5588_gpio_remove(struct i2c_client *client) @@ -460,7 +416,7 @@ static int adp5588_gpio_remove(struct i2c_client *client) struct adp5588_gpio *dev = i2c_get_clientdata(client); int ret; - if (pdata->teardown) { + if (pdata && pdata->teardown) { ret = pdata->teardown(client, dev->gpio_chip.base, dev->gpio_chip.ngpio, pdata->context); @@ -470,7 +426,7 @@ static int adp5588_gpio_remove(struct i2c_client *client) } } - if (dev->irq_base) + if (dev->client->irq) free_irq(dev->client->irq, dev); return 0; @@ -480,14 +436,22 @@ static const struct i2c_device_id adp5588_gpio_id[] = { {DRV_NAME, 0}, {} }; - MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); +#ifdef CONFIG_OF +static const struct of_device_id adp5588_gpio_of_id[] = { + { .compatible = "adi," DRV_NAME, }, + {}, +}; +MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); +#endif + static struct i2c_driver adp5588_gpio_driver = { .driver = { - .name = DRV_NAME, - }, - .probe = adp5588_gpio_probe, + .name = DRV_NAME, + .of_match_table = of_match_ptr(adp5588_gpio_of_id), + }, + .probe_new = adp5588_gpio_probe, .remove = adp5588_gpio_remove, .id_table = adp5588_gpio_id, }; diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index 7f9e0304b510..1cea4efccf7c 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -58,19 +58,20 @@ static void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset, static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc, unsigned int nr) { - if (nr >= (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) - return 0; - return -EINVAL; + if (nr < (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) + return -EINVAL; + + return 0; } static int altr_a10sr_gpio_direction_output(struct gpio_chip *gc, unsigned int nr, int value) { - if (nr <= (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) { - altr_a10sr_gpio_set(gc, nr, value); - return 0; - } - return -EINVAL; + if (nr > (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) + return -EINVAL; + + altr_a10sr_gpio_set(gc, nr, value); + return 0; } static const struct gpio_chip altr_a10sr_gc = { diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 8c3ff6e2366f..748fdd4e9a53 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -32,9 +32,9 @@ * struct altera_gpio_chip * @mmchip : memory mapped chip structure. * @gpio_lock : synchronization lock so that new irq/set/get requests - will be blocked until the current one completes. +* will be blocked until the current one completes. * @interrupt_trigger : specifies the hardware configured IRQ trigger type - (rising, falling, both, high) +* (rising, falling, both, high) * @mapped_irq : kernel mapped irq number. */ struct altera_gpio_chip { diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c new file mode 100644 index 000000000000..38c3f4a3d4aa --- /dev/null +++ b/drivers/gpio/gpio-amd-fch.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * GPIO driver for the AMD G series FCH (eg. GX-412TC) + * + * Copyright (C) 2018 metux IT consult + * Author: Enrico Weigelt, metux IT consult <info@metux.net> + * + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio/driver.h> +#include <linux/platform_data/gpio/gpio-amd-fch.h> +#include <linux/spinlock.h> + +#define AMD_FCH_MMIO_BASE 0xFED80000 +#define AMD_FCH_GPIO_BANK0_BASE 0x1500 +#define AMD_FCH_GPIO_SIZE 0x0300 + +#define AMD_FCH_GPIO_FLAG_DIRECTION BIT(23) +#define AMD_FCH_GPIO_FLAG_WRITE BIT(22) +#define AMD_FCH_GPIO_FLAG_READ BIT(16) + +static struct resource amd_fch_gpio_iores = + DEFINE_RES_MEM_NAMED( + AMD_FCH_MMIO_BASE + AMD_FCH_GPIO_BANK0_BASE, + AMD_FCH_GPIO_SIZE, + "amd-fch-gpio-iomem"); + +struct amd_fch_gpio_priv { + struct platform_device *pdev; + struct gpio_chip gc; + void __iomem *base; + struct amd_fch_gpio_pdata *pdata; + spinlock_t lock; +}; + +static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv, + unsigned int gpio) +{ + return priv->base + priv->pdata->gpio_reg[gpio]*sizeof(u32); +} + +static int amd_fch_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, offset); + + spin_lock_irqsave(&priv->lock, flags); + writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int amd_fch_gpio_direction_output(struct gpio_chip *gc, + unsigned int gpio, int value) +{ + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + + val = readl_relaxed(ptr); + if (value) + val |= AMD_FCH_GPIO_FLAG_WRITE; + else + val &= ~AMD_FCH_GPIO_FLAG_WRITE; + + writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + int ret; + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); + + spin_lock_irqsave(&priv->lock, flags); + ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static void amd_fch_gpio_set(struct gpio_chip *gc, + unsigned int gpio, int value) +{ + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); + u32 mask; + + spin_lock_irqsave(&priv->lock, flags); + + mask = readl_relaxed(ptr); + if (value) + mask |= AMD_FCH_GPIO_FLAG_WRITE; + else + mask &= ~AMD_FCH_GPIO_FLAG_WRITE; + writel_relaxed(mask, ptr); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int amd_fch_gpio_get(struct gpio_chip *gc, + unsigned int offset) +{ + unsigned long flags; + int ret; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, offset); + + spin_lock_irqsave(&priv->lock, flags); + ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_READ); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int amd_fch_gpio_request(struct gpio_chip *chip, + unsigned int gpio_pin) +{ + return 0; +} + +static int amd_fch_gpio_probe(struct platform_device *pdev) +{ + struct amd_fch_gpio_priv *priv; + struct amd_fch_gpio_pdata *pdata; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "no platform_data\n"); + return -ENOENT; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdata = pdata; + priv->pdev = pdev; + + priv->gc.owner = THIS_MODULE; + priv->gc.parent = &pdev->dev; + priv->gc.label = dev_name(&pdev->dev); + priv->gc.ngpio = priv->pdata->gpio_num; + priv->gc.names = priv->pdata->gpio_names; + priv->gc.base = -1; + priv->gc.request = amd_fch_gpio_request; + priv->gc.direction_input = amd_fch_gpio_direction_input; + priv->gc.direction_output = amd_fch_gpio_direction_output; + priv->gc.get_direction = amd_fch_gpio_get_direction; + priv->gc.get = amd_fch_gpio_get; + priv->gc.set = amd_fch_gpio_set; + + spin_lock_init(&priv->lock); + + priv->base = devm_ioremap_resource(&pdev->dev, &amd_fch_gpio_iores); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + platform_set_drvdata(pdev, priv); + + return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); +} + +static struct platform_driver amd_fch_gpio_driver = { + .driver = { + .name = AMD_FCH_GPIO_DRIVER_NAME, + }, + .probe = amd_fch_gpio_probe, +}; + +module_platform_driver(amd_fch_gpio_driver); + +MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); +MODULE_DESCRIPTION("AMD G-series FCH GPIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" AMD_FCH_GPIO_DRIVER_NAME); diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 58531d8b8c6e..14d1f4c933b6 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -1,28 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * gpio-crystalcove.c - Intel Crystal Cove GPIO Driver + * Intel Crystal Cove GPIO Driver * * Copyright (C) 2012, 2014 Intel Corporation. All rights reserved. * - * 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 in the hope that 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. - * * Author: Yang, Bin <bin.yang@intel.com> */ +#include <linux/bitops.h> +#include <linux/gpio/driver.h> #include <linux/interrupt.h> +#include <linux/mfd/intel_soc_pmic.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/gpio/driver.h> -#include <linux/seq_file.h> -#include <linux/bitops.h> #include <linux/regmap.h> -#include <linux/mfd/intel_soc_pmic.h> +#include <linux/seq_file.h> #define CRYSTALCOVE_GPIO_NUM 16 #define CRYSTALCOVE_VGPIO_NUM 95 @@ -279,8 +271,8 @@ static struct irq_chip crystalcove_irqchip = { static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) { struct crystalcove_gpio *cg = data; + unsigned long pending; unsigned int p0, p1; - int pending; int gpio; unsigned int virq; @@ -293,11 +285,9 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) pending = p0 | p1 << 8; - for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) { - if (pending & BIT(gpio)) { - virq = irq_find_mapping(cg->chip.irq.domain, gpio); - handle_nested_irq(virq); - } + for_each_set_bit(gpio, &pending, CRYSTALCOVE_GPIO_NUM) { + virq = irq_find_mapping(cg->chip.irq.domain, gpio); + handle_nested_irq(virq); } return IRQ_HANDLED; diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index bdb29e51b417..188b8e5c8e67 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -198,7 +198,6 @@ static int davinci_gpio_probe(struct platform_device *pdev) struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; struct device *dev = &pdev->dev; - struct resource *res; pdata = davinci_gpio_get_pdata(pdev); if (!pdata) { @@ -236,8 +235,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) if (!chips) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gpio_base = devm_ioremap_resource(dev, res); + gpio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gpio_base)) return PTR_ERR(gpio_base); diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index e41223c05f6e..f0223cee9774 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -432,6 +432,7 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) default: return -ENOTSUPP; } + break; default: dev_err(chip->parent, "Unsupported EIC type.\n"); return -ENOTSUPP; diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 13350c9d7f5e..0896c825b312 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -39,8 +39,10 @@ #define SIO_F71889_ID 0x0909 /* F71889 chipset ID */ #define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */ #define SIO_F81866_ID 0x1010 /* F81866 chipset ID */ +#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */ -enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866 }; + +enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866, f81804 }; static const char * const f7188x_names[] = { "f71869", @@ -49,6 +51,7 @@ static const char * const f7188x_names[] = { "f71889a", "f71889f", "f81866", + "f81804", }; struct f7188x_sio { @@ -223,6 +226,18 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = { F7188X_GPIO_BANK(80, 8, 0x88), }; + +static struct f7188x_gpio_bank f81804_gpio_bank[] = { + F7188X_GPIO_BANK(0, 8, 0xF0), + F7188X_GPIO_BANK(10, 8, 0xE0), + F7188X_GPIO_BANK(20, 8, 0xD0), + F7188X_GPIO_BANK(50, 8, 0xA0), + F7188X_GPIO_BANK(60, 8, 0x90), + F7188X_GPIO_BANK(70, 8, 0x80), + F7188X_GPIO_BANK(90, 8, 0x98), +}; + + static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { int err; @@ -407,6 +422,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev) data->nr_bank = ARRAY_SIZE(f81866_gpio_bank); data->bank = f81866_gpio_bank; break; + case f81804: + data->nr_bank = ARRAY_SIZE(f81804_gpio_bank); + data->bank = f81804_gpio_bank; + break; default: return -ENODEV; } @@ -469,6 +488,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81866_ID: sio->type = f81866; break; + case SIO_F81804_ID: + sio->type = f81804; + break; default: pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid); goto err; diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 95f578804b0e..45fe125823a8 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -41,12 +41,14 @@ * struct ftgpio_gpio - Gemini GPIO state container * @dev: containing device for this instance * @gc: gpiochip for this instance + * @irq: irqchip for this instance * @base: remapped I/O-memory base * @clk: silicon clock */ struct ftgpio_gpio { struct device *dev; struct gpio_chip gc; + struct irq_chip irq; void __iomem *base; struct clk *clk; }; @@ -134,14 +136,6 @@ static int ftgpio_gpio_set_irq_type(struct irq_data *d, unsigned int type) return 0; } -static struct irq_chip ftgpio_gpio_irqchip = { - .name = "FTGPIO010", - .irq_ack = ftgpio_gpio_ack_irq, - .irq_mask = ftgpio_gpio_mask_irq, - .irq_unmask = ftgpio_gpio_unmask_irq, - .irq_set_type = ftgpio_gpio_set_irq_type, -}; - static void ftgpio_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -297,14 +291,20 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) /* Clear any use of debounce */ writel(0x0, g->base + GPIO_DEBOUNCE_EN); - ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip, + g->irq.name = "FTGPIO010"; + g->irq.irq_ack = ftgpio_gpio_ack_irq; + g->irq.irq_mask = ftgpio_gpio_mask_irq; + g->irq.irq_unmask = ftgpio_gpio_unmask_irq; + g->irq.irq_set_type = ftgpio_gpio_set_irq_type; + + ret = gpiochip_irqchip_add(&g->gc, &g->irq, 0, handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_info(dev, "could not add irqchip\n"); goto dis_clk; } - gpiochip_set_chained_irqchip(&g->gc, &ftgpio_gpio_irqchip, + gpiochip_set_chained_irqchip(&g->gc, &g->irq, irq, ftgpio_gpio_irq_handler); platform_set_drvdata(pdev, g); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c new file mode 100644 index 000000000000..242112ff60ee --- /dev/null +++ b/drivers/gpio/gpio-gw-pld.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Gateworks I2C PLD GPIO expander +// +// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> +// +// Based on code and know-how from the OpenWrt driver: +// Copyright (C) 2009 Gateworks Corporation +// Authors: Chris Lang, Imre Kaloz + +#include <linux/bits.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/module.h> + +/** + * struct gw_pld - State container for Gateworks PLD + * @chip: GPIO chip instance + * @client: I2C client + * @out: shadow register for the output bute + */ +struct gw_pld { + struct gpio_chip chip; + struct i2c_client *client; + u8 out; +}; + +/* + * The Gateworks I2C PLD chip only expose one read and one write register. + * Writing a "one" bit (to match the reset state) lets that pin be used as an + * input. It is an open-drain model. + */ +static int gw_pld_input8(struct gpio_chip *gc, unsigned offset) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + + gw->out |= BIT(offset); + return i2c_smbus_write_byte(gw->client, gw->out); +} + +static int gw_pld_get8(struct gpio_chip *gc, unsigned offset) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + s32 val; + + val = i2c_smbus_read_byte(gw->client); + + return (val < 0) ? 0 : !!(val & BIT(offset)); +} + +static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + + if (value) + gw->out |= BIT(offset); + else + gw->out &= ~BIT(offset); + + return i2c_smbus_write_byte(gw->client, gw->out); +} + +static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value) +{ + gw_pld_output8(gc, offset, value); +} + +static int gw_pld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct gw_pld *gw; + int ret; + + gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL); + if (!gw) + return -ENOMEM; + + gw->chip.base = -1; + gw->chip.can_sleep = true; + gw->chip.parent = dev; + gw->chip.of_node = np; + gw->chip.owner = THIS_MODULE; + gw->chip.label = dev_name(dev); + gw->chip.ngpio = 8; + gw->chip.direction_input = gw_pld_input8; + gw->chip.get = gw_pld_get8; + gw->chip.direction_output = gw_pld_output8; + gw->chip.set = gw_pld_set8; + gw->client = client; + + /* + * The Gateworks I2C PLD chip does not properly send the acknowledge + * bit at all times, but we can still use the standard i2c_smbus + * functions by simply ignoring this bit. + */ + client->flags |= I2C_M_IGNORE_NAK; + gw->out = 0xFF; + + i2c_set_clientdata(client, gw); + + ret = devm_gpiochip_add_data(dev, &gw->chip, gw); + if (ret) + return ret; + + dev_info(dev, "registered Gateworks PLD GPIO device\n"); + + return 0; +} + +static const struct i2c_device_id gw_pld_id[] = { + { "gw-pld", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, gw_pld_id); + +static const struct of_device_id gw_pld_dt_ids[] = { + { .compatible = "gateworks,pld-gpio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gw_pld_dt_ids); + +static struct i2c_driver gw_pld_driver = { + .driver = { + .name = "gw_pld", + .of_match_table = gw_pld_dt_ids, + }, + .probe = gw_pld_probe, + .id_table = gw_pld_id, +}; +module_i2c_driver(gw_pld_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index a63136a68ba3..a7b17897356e 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -48,9 +48,163 @@ struct hlwd_gpio { struct gpio_chip gpioc; + struct irq_chip irqc; void __iomem *regs; + int irq; + u32 edge_emulation; + u32 rising_edge, falling_edge; }; +static void hlwd_gpio_irqhandler(struct irq_desc *desc) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_desc_get_handler_data(desc)); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long flags; + unsigned long pending; + int hwirq; + u32 emulated_pending; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); + pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + + /* Treat interrupts due to edge trigger emulation separately */ + emulated_pending = hlwd->edge_emulation & pending; + pending &= ~emulated_pending; + if (emulated_pending) { + u32 level, rising, falling; + + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + rising = level & emulated_pending; + falling = ~level & emulated_pending; + + /* Invert the levels */ + iowrite32be(level ^ emulated_pending, + hlwd->regs + HW_GPIOB_INTLVL); + + /* Ack all emulated-edge interrupts */ + iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); + + /* Signal interrupts only on the correct edge */ + rising &= hlwd->rising_edge; + falling &= hlwd->falling_edge; + + /* Mark emulated interrupts as pending */ + pending |= rising | falling; + } + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + + chained_irq_enter(chip, desc); + + for_each_set_bit(hwirq, &pending, 32) { + int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq); + + generic_handle_irq(irq); + } + + chained_irq_exit(chip, desc); +} + +static void hlwd_gpio_irq_ack(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG); +} + +static void hlwd_gpio_irq_mask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask &= ~BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_unmask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask |= BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_enable(struct irq_data *data) +{ + hlwd_gpio_irq_ack(data); + hlwd_gpio_irq_unmask(data); +} + +static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, + unsigned int flow_type) +{ + u32 level, state; + + /* Set the trigger level to the inactive level */ + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); + level &= ~BIT(hwirq); + level |= state ^ BIT(hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + + hlwd->edge_emulation |= BIT(hwirq); + hlwd->rising_edge &= ~BIT(hwirq); + hlwd->falling_edge &= ~BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_RISING) + hlwd->rising_edge |= BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_FALLING) + hlwd->falling_edge |= BIT(hwirq); +} + +static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 level; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + + hlwd->edge_emulation &= ~BIT(data->hwirq); + + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level |= BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + case IRQ_TYPE_LEVEL_LOW: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level &= ~BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); + break; + default: + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return 0; +} + static int hlwd_gpio_probe(struct platform_device *pdev) { struct hlwd_gpio *hlwd; @@ -92,7 +246,43 @@ static int hlwd_gpio_probe(struct platform_device *pdev) ngpios = 32; hlwd->gpioc.ngpio = ngpios; - return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + if (res) + return res; + + /* Mask and ack all interrupts */ + iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); + iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); + + /* + * If this GPIO controller is not marked as an interrupt controller in + * the DT, return. + */ + if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) + return 0; + + hlwd->irq = platform_get_irq(pdev, 0); + if (hlwd->irq < 0) { + dev_info(&pdev->dev, "platform_get_irq returned %d\n", + hlwd->irq); + return hlwd->irq; + } + + hlwd->irqc.name = dev_name(&pdev->dev); + hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; + hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; + hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; + hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; + + res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0, + handle_level_irq, IRQ_TYPE_NONE); + if (res) + return res; + + gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc, + hlwd->irq, hlwd_gpio_irqhandler); + + return 0; } static const struct of_device_id hlwd_gpio_match[] = { diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index 7ba68d1a0932..c9dad0543672 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -107,7 +107,7 @@ static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset, MADERA_GPIO1_CTRL_1 + reg_offset, ret); } -static struct gpio_chip madera_gpio_chip = { +static const struct gpio_chip madera_gpio_chip = { .label = "madera", .owner = THIS_MODULE, .request = gpiochip_generic_request, diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 6a50f9f59c90..154d959e8993 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -47,6 +47,7 @@ enum { struct gpio_mockup_line_status { int dir; int value; + int pull; }; struct gpio_mockup_chip { @@ -54,12 +55,13 @@ struct gpio_mockup_chip { struct gpio_mockup_line_status *lines; struct irq_sim irqsim; struct dentry *dbg_dir; + struct mutex lock; }; struct gpio_mockup_dbgfs_private { struct gpio_mockup_chip *chip; struct gpio_desc *desc; - int offset; + unsigned int offset; }; static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; @@ -82,29 +84,66 @@ static int gpio_mockup_range_ngpio(unsigned int index) return gpio_mockup_ranges[index * 2 + 1]; } +static int __gpio_mockup_get(struct gpio_mockup_chip *chip, + unsigned int offset) +{ + return chip->lines[offset].value; +} + static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + int val; - return chip->lines[offset].value; + mutex_lock(&chip->lock); + val = __gpio_mockup_get(chip, offset); + mutex_unlock(&chip->lock); + + return val; } -static void gpio_mockup_set(struct gpio_chip *gc, - unsigned int offset, int value) +static int gpio_mockup_get_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + unsigned int bit, val; + + mutex_lock(&chip->lock); + for_each_set_bit(bit, mask, gc->ngpio) { + val = __gpio_mockup_get(chip, bit); + __assign_bit(bit, bits, val); + } + mutex_unlock(&chip->lock); + + return 0; +} +static void __gpio_mockup_set(struct gpio_mockup_chip *chip, + unsigned int offset, int value) +{ chip->lines[offset].value = !!value; } +static void gpio_mockup_set(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + __gpio_mockup_set(chip, offset, value); + mutex_unlock(&chip->lock); +} + static void gpio_mockup_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); unsigned int bit; + mutex_lock(&chip->lock); for_each_set_bit(bit, mask, gc->ngpio) - gpio_mockup_set(gc, bit, test_bit(bit, bits)); - + __gpio_mockup_set(chip, bit, test_bit(bit, bits)); + mutex_unlock(&chip->lock); } static int gpio_mockup_dirout(struct gpio_chip *gc, @@ -112,8 +151,10 @@ static int gpio_mockup_dirout(struct gpio_chip *gc, { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - gpio_mockup_set(gc, offset, value); + mutex_lock(&chip->lock); chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT; + __gpio_mockup_set(chip, offset, value); + mutex_unlock(&chip->lock); return 0; } @@ -122,7 +163,9 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + mutex_lock(&chip->lock); chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN; + mutex_unlock(&chip->lock); return 0; } @@ -130,8 +173,13 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + int direction; - return !chip->lines[offset].dir; + mutex_lock(&chip->lock); + direction = !chip->lines[offset].dir; + mutex_unlock(&chip->lock); + + return direction; } static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) @@ -141,15 +189,56 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) return irq_sim_irqnum(&chip->irqsim, offset); } -static ssize_t gpio_mockup_event_write(struct file *file, - const char __user *usr_buf, - size_t size, loff_t *ppos) +static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + __gpio_mockup_set(chip, offset, chip->lines[offset].pull); +} + +static ssize_t gpio_mockup_debugfs_read(struct file *file, + char __user *usr_buf, + size_t size, loff_t *ppos) { struct gpio_mockup_dbgfs_private *priv; struct gpio_mockup_chip *chip; struct seq_file *sfile; + struct gpio_chip *gc; + char buf[3]; + int val, rv; + + if (*ppos != 0) + return 0; + + sfile = file->private_data; + priv = sfile->private; + chip = priv->chip; + gc = &chip->gc; + + val = gpio_mockup_get(gc, priv->offset); + snprintf(buf, sizeof(buf), "%d\n", val); + + rv = copy_to_user(usr_buf, buf, sizeof(buf)); + if (rv) + return rv; + + return sizeof(buf) - 1; +} + +static ssize_t gpio_mockup_debugfs_write(struct file *file, + const char __user *usr_buf, + size_t size, loff_t *ppos) +{ + struct gpio_mockup_dbgfs_private *priv; + int rv, val, curr, irq, irq_type; + struct gpio_mockup_chip *chip; + struct seq_file *sfile; struct gpio_desc *desc; - int rv, val; + struct gpio_chip *gc; + struct irq_sim *sim; + + if (*ppos != 0) + return -EINVAL; rv = kstrtoint_from_user(usr_buf, size, 0, &val); if (rv) @@ -159,24 +248,70 @@ static ssize_t gpio_mockup_event_write(struct file *file, sfile = file->private_data; priv = sfile->private; - desc = priv->desc; chip = priv->chip; + gc = &chip->gc; + desc = &gc->gpiodev->descs[priv->offset]; + sim = &chip->irqsim; + + mutex_lock(&chip->lock); + + if (test_bit(FLAG_REQUESTED, &desc->flags) && + !test_bit(FLAG_IS_OUT, &desc->flags)) { + curr = __gpio_mockup_get(chip, priv->offset); + if (curr == val) + goto out; + + irq = irq_sim_irqnum(sim, priv->offset); + irq_type = irq_get_trigger_type(irq); + + if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) || + (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) + irq_sim_fire(sim, priv->offset); + } + + /* Change the value unless we're actively driving the line. */ + if (!test_bit(FLAG_REQUESTED, &desc->flags) || + !test_bit(FLAG_IS_OUT, &desc->flags)) + __gpio_mockup_set(chip, priv->offset, val); - gpiod_set_value_cansleep(desc, val); - irq_sim_fire(&chip->irqsim, priv->offset); +out: + chip->lines[priv->offset].pull = val; + mutex_unlock(&chip->lock); return size; } -static int gpio_mockup_event_open(struct inode *inode, struct file *file) +static int gpio_mockup_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, NULL, inode->i_private); } -static const struct file_operations gpio_mockup_event_ops = { +/* + * Each mockup chip is represented by a directory named after the chip's device + * name under /sys/kernel/debug/gpio-mockup/. Each line is represented by + * a file using the line's offset as the name under the chip's directory. + * + * Reading from the line's file yields the current *value*, writing to the + * line's file changes the current *pull*. Default pull for mockup lines is + * down. + * + * Examples: + * - when a line pulled down is requested in output mode and driven high, its + * value will return to 0 once it's released + * - when the line is requested in output mode and driven high, writing 0 to + * the corresponding debugfs file will change the pull to down but the + * reported value will still be 1 until the line is released + * - line requested in input mode always reports the same value as its pull + * configuration + * - when the line is requested in input mode and monitored for events, writing + * the same value to the debugfs file will be a noop, while writing the + * opposite value will generate a dummy interrupt with an appropriate edge + */ +static const struct file_operations gpio_mockup_debugfs_ops = { .owner = THIS_MODULE, - .open = gpio_mockup_event_open, - .write = gpio_mockup_event_write, + .open = gpio_mockup_debugfs_open, + .read = gpio_mockup_debugfs_read, + .write = gpio_mockup_debugfs_write, .llseek = no_llseek, }; @@ -184,7 +319,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, struct gpio_mockup_chip *chip) { struct gpio_mockup_dbgfs_private *priv; - struct dentry *evfile, *link; + struct dentry *evfile; struct gpio_chip *gc; const char *devname; char *name; @@ -197,10 +332,6 @@ static void gpio_mockup_debugfs_setup(struct device *dev, if (IS_ERR_OR_NULL(chip->dbg_dir)) goto err; - link = debugfs_create_symlink(gc->label, gpio_mockup_dbg_dir, devname); - if (IS_ERR_OR_NULL(link)) - goto err; - for (i = 0; i < gc->ngpio; i++) { name = devm_kasprintf(dev, GFP_KERNEL, "%d", i); if (!name) @@ -215,7 +346,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, priv->desc = &gc->gpiodev->descs[i]; evfile = debugfs_create_file(name, 0200, chip->dbg_dir, priv, - &gpio_mockup_event_ops); + &gpio_mockup_debugfs_ops); if (IS_ERR_OR_NULL(evfile)) goto err; } @@ -223,7 +354,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, return; err: - dev_err(dev, "error creating debugfs event files\n"); + dev_err(dev, "error creating debugfs files\n"); } static int gpio_mockup_name_lines(struct device *dev, @@ -283,6 +414,8 @@ static int gpio_mockup_probe(struct platform_device *pdev) return -ENOMEM; } + mutex_init(&chip->lock); + gc = &chip->gc; gc->base = base; gc->ngpio = ngpio; @@ -291,11 +424,13 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->parent = dev; gc->get = gpio_mockup_get; gc->set = gpio_mockup_set; + gc->get_multiple = gpio_mockup_get_multiple; gc->set_multiple = gpio_mockup_set_multiple; gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; gc->to_irq = gpio_mockup_to_irq; + gc->free = gpio_mockup_free; chip->lines = devm_kcalloc(dev, gc->ngpio, sizeof(*chip->lines), GFP_KERNEL); @@ -369,7 +504,7 @@ static int __init gpio_mockup_init(void) return -EINVAL; } - gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL); if (IS_ERR_OR_NULL(gpio_mockup_dbg_dir)) gpio_mockup_err("error creating debugfs directory\n"); diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 3b34dbecef99..7e3c96e4ab2c 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -1,32 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Medfield MSIC GPIO driver> * Copyright (c) 2011, Intel Corporation. * * Author: Mathias Nyman <mathias.nyman@linux.intel.com> * Based on intel_pmic_gpio.c - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/init.h> #include <linux/gpio/driver.h> -#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> #include <linux/mfd/intel_msic.h> +#include <linux/platform_device.h> +#include <linux/slab.h> /* the offset for the mapping of global gpio pin to irq */ #define MSIC_GPIO_IRQ_OFFSET 0x100 @@ -237,20 +224,17 @@ static void msic_gpio_irq_handler(struct irq_desc *desc) struct msic_gpio *mg = irq_data_get_irq_handler_data(data); struct irq_chip *chip = irq_data_get_irq_chip(data); struct intel_msic *msic = pdev_to_intel_msic(mg->pdev); + unsigned long pending; int i; int bitnr; u8 pin; - unsigned long pending = 0; for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) { intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin); pending = pin; - if (pending) { - for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) - generic_handle_irq(mg->irq_base + - (i * BITS_PER_BYTE) + bitnr); - } + for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) + generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr); } chip->irq_eoi(data); } diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 7d5c55494ccd..f97ed32b8beb 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -376,6 +376,16 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, return 0; } +static int mvebu_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); + u32 u; + + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); + + return !!(u & BIT(pin)); +} + static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); @@ -1130,6 +1140,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.parent = &pdev->dev; mvchip->chip.request = gpiochip_generic_request; mvchip->chip.free = gpiochip_generic_free; + mvchip->chip.get_direction = mvebu_gpio_get_direction; mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 2d1dfa1e0745..e86e61dda4b7 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -438,8 +438,11 @@ static int mxc_gpio_probe(struct platform_device *pdev) /* the controller clock is optional */ port->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(port->clk)) + if (IS_ERR(port->clk)) { + if (PTR_ERR(port->clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; port->clk = NULL; + } err = clk_prepare_enable(port->clk); if (err) { diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index f4e9921fa966..7f33024b6d83 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -883,14 +883,16 @@ static void omap_gpio_unmask_irq(struct irq_data *d) if (trigger) omap_set_gpio_triggering(bank, offset, trigger); - /* For level-triggered GPIOs, the clearing must be done after - * the HW source is cleared, thus after the handler has run */ - if (bank->level_mask & BIT(offset)) { - omap_set_gpio_irqenable(bank, offset, 0); + omap_set_gpio_irqenable(bank, offset, 1); + + /* + * For level-triggered GPIOs, clearing must be done after the source + * is cleared, thus after the handler has run. OMAP4 needs this done + * after enabing the interrupt to clear the wakeup status. + */ + if (bank->level_mask & BIT(offset)) omap_clear_gpio_irqstatus(bank, offset); - } - omap_set_gpio_irqenable(bank, offset, 1); raw_spin_unlock_irqrestore(&bank->lock, flags); } diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 0dc96419efe3..7e76830b3368 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -65,7 +65,7 @@ #define PCA_INT 0x0100 #define PCA_PCAL 0x0200 -#define PCA_LATCH_INT (PCA_PCAL | PCA_INT) +#define PCA_LATCH_INT (PCA_PCAL | PCA_INT) #define PCA953X_TYPE 0x1000 #define PCA957X_TYPE 0x2000 #define PCA_TYPE_MASK 0xF000 @@ -88,8 +88,9 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, - { "pcal6524", 24 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, - { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, + { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "max7310", 8 | PCA953X_TYPE, }, { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, @@ -108,7 +109,7 @@ static const struct i2c_device_id pca953x_id[] = { MODULE_DEVICE_TABLE(i2c, pca953x_id); static const struct acpi_device_id pca953x_acpi_ids[] = { - { "INT3491", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, + { "INT3491", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { } }; MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids); @@ -150,6 +151,7 @@ struct pca953x_chip { u8 irq_stat[MAX_BANK]; u8 irq_trig_raise[MAX_BANK]; u8 irq_trig_fall[MAX_BANK]; + struct irq_chip irq_chip; #endif struct i2c_client *client; @@ -178,6 +180,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) #define PCA957x_BANK_OUTPUT BIT(5) #define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2) +#define PCAL9xxx_BANK_PULL_EN BIT(8 + 3) +#define PCAL9xxx_BANK_PULL_SEL BIT(8 + 4) #define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5) #define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6) @@ -199,6 +203,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) * - Extended set, above 0x40, often chip specific. * - PCAL6524/PCAL9555A with custom PCAL IRQ handling: * Input latch register 0x40 + 2 * bank_size RW + * Pull-up/pull-down enable reg 0x40 + 3 * bank_size RW + * Pull-up/pull-down select reg 0x40 + 4 * bank_size RW * Interrupt mask register 0x40 + 5 * bank_size RW * Interrupt status register 0x40 + 6 * bank_size R * @@ -247,7 +253,8 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) { - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK | + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK | PCAL9xxx_BANK_IRQ_STAT; } @@ -268,7 +275,8 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK; + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK; return pca953x_check_register(chip, reg, bank); } @@ -473,6 +481,61 @@ exit: mutex_unlock(&chip->i2c_lock); } +static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, + unsigned int offset, + unsigned long config) +{ + u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset, + true, false); + u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset, + true, false); + u8 bit = BIT(offset % BANK_SZ); + int ret; + + /* + * pull-up/pull-down configuration requires PCAL extended + * registers + */ + if (!(chip->driver_data & PCA_PCAL)) + return -ENOTSUPP; + + mutex_lock(&chip->i2c_lock); + + /* Disable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + if (ret) + goto exit; + + /* Configure pull-up/pull-down */ + if (config == PIN_CONFIG_BIAS_PULL_UP) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + else if (config == PIN_CONFIG_BIAS_PULL_DOWN) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + if (ret) + goto exit; + + /* Enable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + +exit: + mutex_unlock(&chip->i2c_lock); + return ret; +} + +static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct pca953x_chip *chip = gpiochip_get_data(gc); + + switch (config) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + return pca953x_gpio_set_pull_up_down(chip, offset, config); + default: + return -ENOTSUPP; + } +} + static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) { struct gpio_chip *gc; @@ -485,6 +548,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->set = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->set_multiple = pca953x_gpio_set_multiple; + gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; gc->base = chip->gpio_start; @@ -512,6 +576,14 @@ static void pca953x_irq_unmask(struct irq_data *d) chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ); } +static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct pca953x_chip *chip = gpiochip_get_data(gc); + + return irq_set_irq_wake(chip->client->irq, on); +} + static void pca953x_irq_bus_lock(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -587,23 +659,14 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) static void pca953x_irq_shutdown(struct irq_data *d) { - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct pca953x_chip *chip = gpiochip_get_data(gc); u8 mask = 1 << (d->hwirq % BANK_SZ); chip->irq_trig_raise[d->hwirq / BANK_SZ] &= ~mask; chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask; } -static struct irq_chip pca953x_irq_chip = { - .name = "pca953x", - .irq_mask = pca953x_irq_mask, - .irq_unmask = pca953x_irq_unmask, - .irq_bus_lock = pca953x_irq_bus_lock, - .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock, - .irq_set_type = pca953x_irq_set_type, - .irq_shutdown = pca953x_irq_shutdown, -}; - static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) { u8 cur_stat[MAX_BANK]; @@ -699,56 +762,65 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) { struct i2c_client *client = chip->client; + struct irq_chip *irq_chip = &chip->irq_chip; int reg_direction[MAX_BANK]; int ret, i; - if (client->irq && irq_base != -1 - && (chip->driver_data & PCA_INT)) { - ret = pca953x_read_regs(chip, - chip->regs->input, chip->irq_stat); - if (ret) - return ret; + if (!client->irq) + return 0; - /* - * There is no way to know which GPIO line generated the - * interrupt. We have to rely on the previous read for - * this purpose. - */ - regmap_bulk_read(chip->regmap, chip->regs->direction, - reg_direction, NBANK(chip)); - for (i = 0; i < NBANK(chip); i++) - chip->irq_stat[i] &= reg_direction[i]; - mutex_init(&chip->irq_lock); - - ret = devm_request_threaded_irq(&client->dev, - client->irq, - NULL, - pca953x_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | - IRQF_SHARED, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - return ret; - } + if (irq_base == -1) + return 0; - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, - &pca953x_irq_chip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } + if (!(chip->driver_data & PCA_INT)) + return 0; - gpiochip_set_nested_irqchip(&chip->gpio_chip, - &pca953x_irq_chip, - client->irq); + ret = pca953x_read_regs(chip, chip->regs->input, chip->irq_stat); + if (ret) + return ret; + + /* + * There is no way to know which GPIO line generated the + * interrupt. We have to rely on the previous read for + * this purpose. + */ + regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction, + NBANK(chip)); + for (i = 0; i < NBANK(chip); i++) + chip->irq_stat[i] &= reg_direction[i]; + mutex_init(&chip->irq_lock); + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pca953x_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | + IRQF_SHARED, + dev_name(&client->dev), chip); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + return ret; } + irq_chip->name = dev_name(&chip->client->dev); + irq_chip->irq_mask = pca953x_irq_mask; + irq_chip->irq_unmask = pca953x_irq_unmask; + irq_chip->irq_set_wake = pca953x_irq_set_wake; + irq_chip->irq_bus_lock = pca953x_irq_bus_lock; + irq_chip->irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock; + irq_chip->irq_set_type = pca953x_irq_set_type; + irq_chip->irq_shutdown = pca953x_irq_shutdown; + + ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip, + irq_base, handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&client->dev, + "could not connect irqchip to gpiochip\n"); + return ret; + } + + gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq); + return 0; } diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 68a35b65925a..c9b650f617fa 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -89,7 +89,6 @@ struct pcf857x { struct mutex lock; /* protect 'out' */ unsigned out; /* software latch */ unsigned status; /* current status */ - unsigned int irq_parent; unsigned irq_enabled; /* enabled irqs */ int (*write)(struct i2c_client *client, unsigned data); @@ -211,18 +210,7 @@ static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on) { struct pcf857x *gpio = irq_data_get_irq_chip_data(data); - int error = 0; - - if (gpio->irq_parent) { - error = irq_set_irq_wake(gpio->irq_parent, on); - if (error) { - dev_dbg(&gpio->client->dev, - "irq %u doesn't support irq_set_wake\n", - gpio->irq_parent); - gpio->irq_parent = 0; - } - } - return error; + return irq_set_irq_wake(gpio->client->irq, on); } static void pcf857x_irq_enable(struct irq_data *data) @@ -392,7 +380,6 @@ static int pcf857x_probe(struct i2c_client *client, gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip, client->irq); - gpio->irq_parent = client->irq; } /* Let platform code set up the GPIOs and their users. diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 29e044ff4b17..24228cf79afc 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -322,7 +322,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, pmic_eic->irq, NULL, sprd_pmic_eic_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND, dev_name(&pdev->dev), pmic_eic); if (ret) { @@ -365,7 +364,7 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) } static const struct of_device_id sprd_pmic_eic_of_match[] = { - { .compatible = "sprd,sc27xx-eic", }, + { .compatible = "sprd,sc2731-eic", }, { /* end of list */ } }; MODULE_DEVICE_TABLE(of, sprd_pmic_eic_of_match); diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 068ce25ffd28..500a3596aaf4 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -40,6 +40,7 @@ struct gpio_rcar_priv { struct irq_chip irq_chip; unsigned int irq_parent; atomic_t wakeup_path; + bool has_outdtsel; bool has_both_edge_trigger; struct gpio_rcar_bank_info bank_info; }; @@ -55,6 +56,7 @@ struct gpio_rcar_priv { #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ #define EDGLEVEL 0x24 /* Edge/level Select Register */ #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ +#define OUTDTSEL 0x40 /* Output Data Select Register */ #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ #define RCAR_MAX_GPIO_PER_BANK 32 @@ -235,6 +237,10 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, /* Select Input Mode or Output Mode in INOUTSEL */ gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); + /* Select General Output Register to output data in OUTDTSEL */ + if (p->has_outdtsel && output) + gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); + spin_unlock_irqrestore(&p->lock, flags); } @@ -336,14 +342,17 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, } struct gpio_rcar_info { + bool has_outdtsel; bool has_both_edge_trigger; }; static const struct gpio_rcar_info gpio_rcar_info_gen1 = { + .has_outdtsel = false, .has_both_edge_trigger = false, }; static const struct gpio_rcar_info gpio_rcar_info_gen2 = { + .has_outdtsel = true, .has_both_edge_trigger = true, }; @@ -403,10 +412,11 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) int ret; info = of_device_get_match_data(p->dev); + p->has_outdtsel = info->has_outdtsel; + p->has_both_edge_trigger = info->has_both_edge_trigger; ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; - p->has_both_edge_trigger = info->has_both_edge_trigger; if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 03a000659fa1..7d718557092e 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -109,16 +109,6 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, } /** - * sama5d2_piobu_set_direction() - mark pin as input or output - */ -static int sama5d2_piobu_set_direction(struct gpio_chip *chip, - unsigned int direction, - unsigned int pin) -{ - return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction); -} - -/** * sama5d2_piobu_get_direction() - gpiochip get_direction */ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, @@ -138,7 +128,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, static int sama5d2_piobu_direction_input(struct gpio_chip *chip, unsigned int pin) { - return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin); + return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); } /** @@ -147,7 +137,13 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, static int sama5d2_piobu_direction_output(struct gpio_chip *chip, unsigned int pin, int value) { - return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin); + unsigned int val = PIOBU_OUT; + + if (value) + val |= PIOBU_HIGH; + + return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD, + val); } /** diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 02f6db925fd5..1ececf2c3282 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/gpio.c * * Copyright (c) 2010 Google, Inc + * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. * * Author: * Erik Gilling <konkers@google.com> @@ -141,14 +142,14 @@ static void tegra_gpio_disable(struct tegra_gpio_info *tgi, unsigned int gpio) static int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset) { - return pinctrl_gpio_request(offset); + return pinctrl_gpio_request(chip->base + offset); } static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - pinctrl_gpio_free(offset); + pinctrl_gpio_free(chip->base + offset); tegra_gpio_disable(tgi, offset); } @@ -176,10 +177,18 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + int ret; tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); - return 0; + + ret = pinctrl_gpio_direction_input(chip->base + offset); + if (ret < 0) + dev_err(tgi->dev, + "Failed to set pinctrl input direction of GPIO %d: %d", + chip->base + offset, ret); + + return ret; } static int tegra_gpio_direction_output(struct gpio_chip *chip, @@ -187,11 +196,19 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + int ret; tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); - return 0; + + ret = pinctrl_gpio_direction_output(chip->base + offset); + if (ret < 0) + dev_err(tgi->dev, + "Failed to set pinctrl output direction of GPIO %d: %d", + chip->base + offset, ret); + + return ret; } static int tegra_gpio_get_direction(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 66ec38bb7954..7d42e3d7572c 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -529,8 +529,8 @@ static int tegra186_gpio_remove(struct platform_device *pdev) return 0; } -#define TEGRA_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA_MAIN_GPIO_PORT_##port] = { \ +#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ + [TEGRA186_MAIN_GPIO_PORT_##port] = { \ .name = #port, \ .offset = base, \ .pins = count, \ @@ -538,29 +538,29 @@ static int tegra186_gpio_remove(struct platform_device *pdev) } static const struct tegra_gpio_port tegra186_main_ports[] = { - TEGRA_MAIN_GPIO_PORT( A, 0x2000, 7, 2), - TEGRA_MAIN_GPIO_PORT( B, 0x3000, 7, 3), - TEGRA_MAIN_GPIO_PORT( C, 0x3200, 7, 3), - TEGRA_MAIN_GPIO_PORT( D, 0x3400, 6, 3), - TEGRA_MAIN_GPIO_PORT( E, 0x2200, 8, 2), - TEGRA_MAIN_GPIO_PORT( F, 0x2400, 6, 2), - TEGRA_MAIN_GPIO_PORT( G, 0x4200, 6, 4), - TEGRA_MAIN_GPIO_PORT( H, 0x1000, 7, 1), - TEGRA_MAIN_GPIO_PORT( I, 0x0800, 8, 0), - TEGRA_MAIN_GPIO_PORT( J, 0x5000, 8, 5), - TEGRA_MAIN_GPIO_PORT( K, 0x5200, 1, 5), - TEGRA_MAIN_GPIO_PORT( L, 0x1200, 8, 1), - TEGRA_MAIN_GPIO_PORT( M, 0x5600, 6, 5), - TEGRA_MAIN_GPIO_PORT( N, 0x0000, 7, 0), - TEGRA_MAIN_GPIO_PORT( O, 0x0200, 4, 0), - TEGRA_MAIN_GPIO_PORT( P, 0x4000, 7, 4), - TEGRA_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), - TEGRA_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), - TEGRA_MAIN_GPIO_PORT( T, 0x0600, 4, 0), - TEGRA_MAIN_GPIO_PORT( X, 0x1400, 8, 1), - TEGRA_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), - TEGRA_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), - TEGRA_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), + TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2), + TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3), + TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3), + TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3), + TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2), + TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2), + TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4), + TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1), + TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0), + TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5), + TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5), + TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1), + TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5), + TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0), + TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0), + TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4), + TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), + TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), + TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0), + TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1), + TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), + TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), + TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), }; static const struct tegra_gpio_soc tegra186_main_soc = { @@ -569,8 +569,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .name = "tegra186-gpio", }; -#define TEGRA_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA_AON_GPIO_PORT_##port] = { \ +#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ + [TEGRA186_AON_GPIO_PORT_##port] = { \ .name = #port, \ .offset = base, \ .pins = count, \ @@ -578,14 +578,14 @@ static const struct tegra_gpio_soc tegra186_main_soc = { } static const struct tegra_gpio_port tegra186_aon_ports[] = { - TEGRA_AON_GPIO_PORT( S, 0x0200, 5, 0), - TEGRA_AON_GPIO_PORT( U, 0x0400, 6, 0), - TEGRA_AON_GPIO_PORT( V, 0x0800, 8, 0), - TEGRA_AON_GPIO_PORT( W, 0x0a00, 8, 0), - TEGRA_AON_GPIO_PORT( Z, 0x0e00, 4, 0), - TEGRA_AON_GPIO_PORT(AA, 0x0c00, 8, 0), - TEGRA_AON_GPIO_PORT(EE, 0x0600, 3, 0), - TEGRA_AON_GPIO_PORT(FF, 0x0000, 5, 0), + TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0), + TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0), + TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0), + TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0), + TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0), + TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0), + TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0), + TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0), }; static const struct tegra_gpio_soc tegra186_aon_soc = { diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c new file mode 100644 index 000000000000..d5880db7f9d4 --- /dev/null +++ b/drivers/gpio/gpio-tqmx86.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TQ-Systems TQMx86 PLD GPIO driver + * + * Based on vendor driver by: + * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru> + */ + +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#define TQMX86_NGPIO 8 +#define TQMX86_NGPO 4 /* 0-3 - output */ +#define TQMX86_NGPI 4 /* 4-7 - input */ +#define TQMX86_DIR_INPUT_MASK 0xf0 /* 0-3 - output, 4-7 - input */ + +#define TQMX86_GPIODD 0 /* GPIO Data Direction Register */ +#define TQMX86_GPIOD 1 /* GPIO Data Register */ +#define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ +#define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ + +#define TQMX86_GPII_FALLING BIT(0) +#define TQMX86_GPII_RISING BIT(1) +#define TQMX86_GPII_MASK (BIT(0) | BIT(1)) +#define TQMX86_GPII_BITS 2 + +struct tqmx86_gpio_data { + struct gpio_chip chip; + struct irq_chip irq_chip; + void __iomem *io_base; + int irq; + raw_spinlock_t spinlock; + u8 irq_type[TQMX86_NGPI]; +}; + +static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg) +{ + return ioread8(gd->io_base + reg); +} + +static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val, + unsigned int reg) +{ + iowrite8(val, gd->io_base + reg); +} + +static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset)); +} + +static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + unsigned long flags; + u8 val; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); + if (value) + val |= BIT(offset); + else + val &= ~BIT(offset); + tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static int tqmx86_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + /* Direction cannot be changed. Validate is an input. */ + if (BIT(offset) & TQMX86_DIR_INPUT_MASK) + return 0; + else + return -EINVAL; +} + +static int tqmx86_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, + int value) +{ + /* Direction cannot be changed, validate is an output */ + if (BIT(offset) & TQMX86_DIR_INPUT_MASK) + return -EINVAL; + + tqmx86_gpio_set(chip, offset, value); + return 0; +} + +static int tqmx86_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return !!(TQMX86_DIR_INPUT_MASK & BIT(offset)); +} + +static void tqmx86_gpio_irq_mask(struct irq_data *data) +{ + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; + u8 gpiic, mask; + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static void tqmx86_gpio_irq_unmask(struct irq_data *data) +{ + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; + u8 gpiic, mask; + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; + gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned int offset = (data->hwirq - TQMX86_NGPO); + unsigned int edge_type = type & IRQF_TRIGGER_MASK; + unsigned long flags; + u8 new_type, gpiic; + + switch (edge_type) { + case IRQ_TYPE_EDGE_RISING: + new_type = TQMX86_GPII_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + new_type = TQMX86_GPII_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; + break; + default: + return -EINVAL; /* not supported */ + } + + gpio->irq_type[offset] = new_type; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); + gpiic |= new_type << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + + return 0; +} + +static void tqmx86_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + unsigned long irq_bits; + int i = 0, child_irq; + u8 irq_status; + + chained_irq_enter(irq_chip, desc); + + irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + irq_bits = irq_status; + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + child_irq = irq_find_mapping(gpio->chip.irq.domain, + i + TQMX86_NGPO); + generic_handle_irq(child_irq); + } + + chained_irq_exit(irq_chip, desc); +} + +/* Minimal runtime PM is needed by the IRQ subsystem */ +static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { + SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, + tqmx86_gpio_runtime_resume, NULL) +}; + +static int tqmx86_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tqmx86_gpio_data *gpio; + struct gpio_chip *chip; + void __iomem *io_base; + struct resource *res; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get I/O\n"); + return -ENODEV; + } + + io_base = devm_ioport_map(&pdev->dev, res->start, resource_size(res)); + if (!io_base) + return -ENOMEM; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + raw_spin_lock_init(&gpio->spinlock); + gpio->io_base = io_base; + + tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = "gpio-tqmx86"; + chip->owner = THIS_MODULE; + chip->can_sleep = false; + chip->base = -1; + chip->direction_input = tqmx86_gpio_direction_input; + chip->direction_output = tqmx86_gpio_direction_output; + chip->get_direction = tqmx86_gpio_get_direction; + chip->get = tqmx86_gpio_get; + chip->set = tqmx86_gpio_set; + chip->ngpio = TQMX86_NGPIO; + chip->irq.need_valid_mask = true; + chip->parent = pdev->dev.parent; + + pm_runtime_enable(&pdev->dev); + + ret = devm_gpiochip_add_data(dev, chip, gpio); + if (ret) { + dev_err(dev, "Could not register GPIO chip\n"); + goto out_pm_dis; + } + + if (irq) { + struct irq_chip *irq_chip = &gpio->irq_chip; + u8 irq_status; + + irq_chip->name = chip->label; + irq_chip->parent_device = &pdev->dev; + irq_chip->irq_mask = tqmx86_gpio_irq_mask; + irq_chip->irq_unmask = tqmx86_gpio_irq_unmask; + irq_chip->irq_set_type = tqmx86_gpio_irq_set_type; + + /* Mask all interrupts */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIIC); + + /* Clear all pending interrupts */ + irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + ret = gpiochip_irqchip_add(chip, irq_chip, + 0, handle_simple_irq, + IRQ_TYPE_EDGE_BOTH); + if (ret) { + dev_err(dev, "Could not add irq chip\n"); + goto out_pm_dis; + } + + gpiochip_set_chained_irqchip(chip, irq_chip, + irq, tqmx86_gpio_irq_handler); + } + + /* Only GPIOs 4-7 are valid for interrupts. Clear the others */ + clear_bit(0, chip->irq.valid_mask); + clear_bit(1, chip->irq.valid_mask); + clear_bit(2, chip->irq.valid_mask); + clear_bit(3, chip->irq.valid_mask); + + dev_info(dev, "GPIO functionality initialized with %d pins\n", + chip->ngpio); + + return 0; + +out_pm_dis: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static struct platform_driver tqmx86_gpio_driver = { + .driver = { + .name = "tqmx86-gpio", + .pm = &tqmx86_gpio_dev_pm_ops, + }, + .probe = tqmx86_gpio_probe, +}; + +module_platform_driver(tqmx86_gpio_driver); + +MODULE_DESCRIPTION("TQMx86 PLD GPIO Driver"); +MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tqmx86-gpio"); diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index dde7c6aecbb5..444fe9e7f04a 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -1,34 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Whiskey Cove PMIC GPIO Driver * * This driver is written based on gpio-crystalcove.c * * Copyright (C) 2016 Intel Corporation. All rights reserved. - * - * 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 in the hope that 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. */ #include <linux/bitops.h> -#include <linux/module.h> -#include <linux/interrupt.h> #include <linux/gpio/driver.h> +#include <linux/interrupt.h> #include <linux/mfd/intel_soc_pmic.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/seq_file.h> /* * Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks: - * Bank 0: Pin 0 - 6 - * Bank 1: Pin 7 - 10 - * Bank 2: Pin 11 -12 + * Bank 0: Pin 0 - 6 + * Bank 1: Pin 7 - 10 + * Bank 2: Pin 11 - 12 * Each pin has one output control register and one input control register. */ #define BANK0_NR_PINS 7 @@ -75,8 +67,8 @@ #define CTLO_RVAL_50KDOWN (2 << 1) #define CTLO_RVAL_50KUP (3 << 1) -#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) -#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) +#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) +#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) enum ctrl_register { CTRL_IN, @@ -105,7 +97,7 @@ struct wcove_gpio { bool set_irq_mask; }; -static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type) +static inline int to_reg(int gpio, enum ctrl_register reg_type) { unsigned int reg; @@ -203,8 +195,7 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) return val & 0x1; } -static void wcove_gpio_set(struct gpio_chip *chip, - unsigned int gpio, int value) +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); diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index b3b4edcdffe0..00ff7b1fa8a1 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -555,6 +555,26 @@ static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int zynq_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int ret; + + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) + return ret; + + return gpiochip_reqres_irq(chip, d->hwirq); +} + +static void zynq_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + gpiochip_relres_irq(chip, d->hwirq); + pm_runtime_put(chip->parent); +} + /* irq chip descriptor */ static struct irq_chip zynq_gpio_level_irqchip = { .name = DRIVER_NAME, @@ -564,6 +584,8 @@ static struct irq_chip zynq_gpio_level_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED | IRQCHIP_MASK_ON_SUSPEND, }; @@ -576,6 +598,8 @@ static struct irq_chip zynq_gpio_edge_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_MASK_ON_SUSPEND, }; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 3e8e2d3ce7a8..30d0baf7ddae 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -29,7 +29,7 @@ * @irq: Linux IRQ number for the event, for request_ / free_irq * @irqflags: flags to pass to request_irq when requesting the IRQ * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source - * @is_requested: True if request_irq has been done + * @irq_requested:True if request_irq has been done * @desc: gpio_desc for the GPIO pin for this event */ struct acpi_gpio_event { @@ -469,6 +469,9 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) static int __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) { + const enum gpiod_flags mask = + GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | + GPIOD_FLAGS_BIT_DIR_VAL; int ret = 0; /* @@ -489,7 +492,7 @@ __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) 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; + *flags = (*flags & ~mask) | (update & mask); } return ret; } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index c34eb9d9c59a..8b9c3ab70f6e 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -86,7 +86,9 @@ static void of_gpio_flags_quirks(struct device_node *np, if (IS_ENABLED(CONFIG_REGULATOR) && (of_device_is_compatible(np, "regulator-fixed") || of_device_is_compatible(np, "reg-fixed-voltage") || - of_device_is_compatible(np, "regulator-gpio"))) { + (of_device_is_compatible(np, "regulator-gpio") && + !(strcmp(propname, "enable-gpio") && + strcmp(propname, "enable-gpios"))))) { /* * The regulator GPIO handles are specified such that the * presence or absence of "enable-active-high" solely controls @@ -345,6 +347,11 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (of_flags & OF_GPIO_TRANSITORY) *flags |= GPIO_TRANSITORY; + if (of_flags & OF_GPIO_PULL_UP) + *flags |= GPIO_PULL_UP; + if (of_flags & OF_GPIO_PULL_DOWN) + *flags |= GPIO_PULL_DOWN; + return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d1adfdf50fb3..144af0733581 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1782,6 +1782,43 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/** + * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * @reserve: If set, only reserve an interrupt vector instead of assigning one + * + * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be + * used as the activate function for the &struct irq_domain_ops. The host_data + * for the IRQ domain must be the &struct gpio_chip. + */ +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_lock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate); + +/** + * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * + * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to + * be used as the deactivate function for the &struct irq_domain_ops. The + * host_data for the IRQ domain must be the &struct gpio_chip. + */ +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_unlock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); + static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { if (!gpiochip_irqchip_irq_valid(chip, offset)) @@ -2525,6 +2562,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ +static int gpio_set_config(struct gpio_chip *gc, unsigned offset, + enum pin_config_param mode) +{ + unsigned long config = { PIN_CONF_PACKED(mode, 0) }; + + return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2572,20 +2617,19 @@ int gpiod_direction_input(struct gpio_desc *desc) if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); + if (test_bit(FLAG_PULL_UP, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_UP); + else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_DOWN); + trace_gpio_direction(desc_to_gpio(desc), 1, status); return status; } EXPORT_SYMBOL_GPL(gpiod_direction_input); -static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset, - enum pin_config_param mode) -{ - unsigned long config = { PIN_CONF_PACKED(mode, 0) }; - - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; -} - static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { struct gpio_chip *gc = desc->gdev->chip; @@ -2682,8 +2726,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) gc = desc->gdev->chip; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { /* First see if we can enable open drain in hardware */ - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_DRAIN); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_DRAIN); if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ @@ -2691,16 +2735,16 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) return gpiod_direction_input(desc); } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_SOURCE); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ if (!value) return gpiod_direction_input(desc); } else { - gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_PUSH_PULL); + gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_PUSH_PULL); } set_output_value: @@ -2732,7 +2776,7 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -2769,7 +2813,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); @@ -4057,6 +4101,17 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + gpiod_err(desc, + "both pull-up and pull-down enabled, invalid configuration\n"); + return -EINVAL; + } + + if (lflags & GPIO_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); + else if (lflags & GPIO_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (status < 0) return status; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index bc57f0dc5953..078ab17b96bf 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -219,6 +219,8 @@ struct gpio_desc { #define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ +#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ +#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ /* Connection label */ const char *label; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1b50fa955992..ad8b1f400919 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -927,7 +927,7 @@ config UCB1400_CORE config MFD_PM8XXX tristate "Qualcomm PM8xxx PMIC chips driver" depends on (ARM || HEXAGON || COMPILE_TEST) - select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY select MFD_CORE select REGMAP help diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index e6e8d81c15fd..8eb2528793f9 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -70,22 +70,23 @@ #define PM8XXX_NR_IRQS 256 #define PM8821_NR_IRQS 112 +struct pm_irq_data { + int num_irqs; + struct irq_chip *irq_chip; + void (*irq_handler)(struct irq_desc *desc); +}; + struct pm_irq_chip { struct regmap *regmap; spinlock_t pm_irq_lock; struct irq_domain *irqdomain; - unsigned int num_irqs; unsigned int num_blocks; unsigned int num_masters; + const struct pm_irq_data *pm_irq_data; + /* MUST BE AT THE END OF THIS STRUCT */ u8 config[0]; }; -struct pm_irq_data { - int num_irqs; - const struct irq_domain_ops *irq_domain_ops; - void (*irq_handler)(struct irq_desc *desc); -}; - static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int *ip) { @@ -375,21 +376,38 @@ static struct irq_chip pm8xxx_irq_chip = { .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; -static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip, + struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq, unsigned int type) { - struct pm_irq_chip *chip = d->host_data; - - irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); - irq_set_chip_data(irq, chip); + irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip, + chip, handle_level_irq, NULL, NULL); irq_set_noprobe(irq); +} + +static int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm_irq_chip *chip = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type); return 0; } static const struct irq_domain_ops pm8xxx_irq_domain_ops = { - .xlate = irq_domain_xlate_twocell, - .map = pm8xxx_irq_domain_map, + .alloc = pm8xxx_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = irq_domain_translate_twocell, }; static void pm8821_irq_mask_ack(struct irq_data *d) @@ -473,23 +491,6 @@ static struct irq_chip pm8821_irq_chip = { .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; -static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct pm_irq_chip *chip = d->host_data; - - irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq); - irq_set_chip_data(irq, chip); - irq_set_noprobe(irq); - - return 0; -} - -static const struct irq_domain_ops pm8821_irq_domain_ops = { - .xlate = irq_domain_xlate_twocell, - .map = pm8821_irq_domain_map, -}; - static const struct regmap_config ssbi_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -501,13 +502,13 @@ static const struct regmap_config ssbi_regmap_config = { static const struct pm_irq_data pm8xxx_data = { .num_irqs = PM8XXX_NR_IRQS, - .irq_domain_ops = &pm8xxx_irq_domain_ops, + .irq_chip = &pm8xxx_irq_chip, .irq_handler = pm8xxx_irq_handler, }; static const struct pm_irq_data pm8821_data = { .num_irqs = PM8821_NR_IRQS, - .irq_domain_ops = &pm8821_irq_domain_ops, + .irq_chip = &pm8821_irq_chip, .irq_handler = pm8821_irq_handler, }; @@ -571,14 +572,14 @@ static int pm8xxx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); chip->regmap = regmap; - chip->num_irqs = data->num_irqs; - chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); + chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8); chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); + chip->pm_irq_data = data; spin_lock_init(&chip->pm_irq_lock); chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, data->num_irqs, - data->irq_domain_ops, + &pm8xxx_irq_domain_ops, chip); if (!chip->irqdomain) return -ENODEV; diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 836e9f3eae4c..2e66ab72c10b 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -137,6 +137,7 @@ config PINCTRL_QCOM_SPMI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, @@ -149,6 +150,7 @@ config PINCTRL_QCOM_SSBI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 4458d44dfcf6..cb512c7a5251 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -12,6 +12,7 @@ */ #include <linux/gpio/driver.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -136,7 +137,6 @@ enum pmic_gpio_func_index { /** * struct pmic_gpio_pad - keep current GPIO settings * @base: Address base in SPMI device. - * @irq: IRQ number which this GPIO generate. * @is_enabled: Set to false when GPIO should be put in high Z state. * @out_value: Cached pin output value * @have_buffer: Set to true if GPIO output could be configured in push-pull, @@ -156,7 +156,6 @@ enum pmic_gpio_func_index { */ struct pmic_gpio_pad { u16 base; - int irq; bool is_enabled; bool out_value; bool have_buffer; @@ -179,6 +178,8 @@ struct pmic_gpio_state { struct regmap *map; struct pinctrl_dev *ctrl; struct gpio_chip chip; + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pmic_gpio_bindings[] = { @@ -761,11 +762,18 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip, static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) { struct pmic_gpio_state *state = gpiochip_get_data(chip); - struct pmic_gpio_pad *pad; + struct irq_fwspec fwspec; - pad = state->ctrl->desc->pins[pin].drv_data; + fwspec.fwnode = state->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET; + /* + * Set the type to a safe value temporarily. This will be overwritten + * later with the proper value by irq_set_type. + */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - return pad->irq; + return irq_create_fwspec_mapping(&fwspec); } static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -935,8 +943,79 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, return 0; } +static struct irq_chip pmic_gpio_irq_chip = { + .name = "spmi-gpio", + .irq_ack = irq_chip_ack_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = irq_chip_set_wake_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +static int pmic_gpio_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + + if (fwspec->param_count != 2 || + fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pmic_gpio_irq_chip, state, + handle_level_irq, NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 4; + parent_fwspec.param[0] = 0; + parent_fwspec.param[1] = hwirq + 0xc0; + parent_fwspec.param[2] = 0; + parent_fwspec.param[3] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pmic_gpio_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pmic_gpio_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pmic_gpio_domain_translate, +}; + static int pmic_gpio_probe(struct platform_device *pdev) { + struct irq_domain *parent_domain; + struct device_node *parent_node; struct device *dev = &pdev->dev; struct pinctrl_pin_desc *pindesc; struct pinctrl_desc *pctrldesc; @@ -951,13 +1030,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) return ret; } - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - - BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); + npins = (uintptr_t) device_get_match_data(&pdev->dev); state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) @@ -999,10 +1072,6 @@ static int pmic_gpio_probe(struct platform_device *pdev) pindesc->number = i; pindesc->name = pmic_gpio_groups[i]; - pad->irq = platform_get_irq(pdev, i); - if (pad->irq < 0) - return pad->irq; - pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; ret = pmic_gpio_populate(state, pad); @@ -1022,10 +1091,28 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (IS_ERR(state->ctrl)) return PTR_ERR(state->ctrl); + parent_node = of_irq_find_parent(state->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + state->fwnode = of_node_to_fwnode(state->dev->of_node); + state->domain = irq_domain_create_hierarchy(parent_domain, 0, + state->chip.ngpio, + state->fwnode, + &pmic_gpio_domain_ops, + &state->chip); + if (!state->domain) + return -ENODEV; + ret = gpiochip_add_data(&state->chip, state); if (ret) { dev_err(state->dev, "can't add gpio chip\n"); - return ret; + goto err_chip_add_data; } /* @@ -1051,6 +1138,8 @@ static int pmic_gpio_probe(struct platform_device *pdev) err_range: gpiochip_remove(&state->chip); +err_chip_add_data: + irq_domain_remove(state->domain); return ret; } @@ -1059,17 +1148,21 @@ static int pmic_gpio_remove(struct platform_device *pdev) struct pmic_gpio_state *state = platform_get_drvdata(pdev); gpiochip_remove(&state->chip); + irq_domain_remove(state->domain); return 0; } static const struct of_device_id pmic_gpio_of_match[] = { - { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ - { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ - { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */ - { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */ - { .compatible = "qcom,spmi-gpio" }, /* Generic */ + { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, + { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, + { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, + { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, + { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, + { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, + /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ + { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, { }, }; diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index ded7d765af2e..08dd62b5cebe 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -55,6 +55,8 @@ #define PM8XXX_MAX_GPIOS 44 +#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 + /* custom pinconf parameters */ #define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) #define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) @@ -99,6 +101,9 @@ struct pm8xxx_gpio { struct pinctrl_desc desc; unsigned npins; + + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { @@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset) if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) { ret = pin->output_value; - } else { + } else if (pin->irq >= 0) { ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state); if (!ret) ret = !!state; - } + } else + ret = -EINVAL; return ret; } @@ -533,7 +539,7 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip, if (flags) *flags = gpio_desc->args[1]; - return gpio_desc->args[0] - 1; + return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; } @@ -541,8 +547,31 @@ static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; + struct irq_fwspec fwspec; + int ret; + + fwspec.fwnode = pctrl->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + ret = irq_create_fwspec_mapping(&fwspec); + + /* + * Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the + * line level. + */ + pin->irq = ret; + + return ret; +} + +static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); + struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; - return pin->irq; + pin->irq = -1; } #ifdef CONFIG_DEBUG_FS @@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s, "no", "high", "medium", "low" }; - seq_printf(s, " gpio%-2d:", offset + 1); + seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); if (pin->disable) { seq_puts(s, " ---"); } else { @@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #endif static const struct gpio_chip pm8xxx_gpio_template = { + .free = pm8xxx_gpio_free, .direction_input = pm8xxx_gpio_direction_input, .direction_output = pm8xxx_gpio_direction_output, .get = pm8xxx_gpio_get, @@ -664,13 +694,75 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, return 0; } +static struct irq_chip pm8xxx_irq_chip = { + .name = "ssbi-gpio", + .irq_mask_ack = irq_chip_mask_ack_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, +}; + +static int pm8xxx_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + + if (fwspec->param_count != 2 || fwspec->param[0] < 1 || + fwspec->param[0] > pctrl->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pm8xxx_irq_chip, pctrl, handle_level_irq, + NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = hwirq + 0xc0; + parent_fwspec.param[1] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pm8xxx_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pm8xxx_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pm8xxx_domain_translate, +}; + static const struct of_device_id pm8xxx_gpio_of_match[] = { - { .compatible = "qcom,pm8018-gpio" }, - { .compatible = "qcom,pm8038-gpio" }, - { .compatible = "qcom,pm8058-gpio" }, - { .compatible = "qcom,pm8917-gpio" }, - { .compatible = "qcom,pm8921-gpio" }, - { .compatible = "qcom,ssbi-gpio" }, + { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, + { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, + { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, + { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, + { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, { }, }; MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); @@ -678,22 +770,18 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); static int pm8xxx_gpio_probe(struct platform_device *pdev) { struct pm8xxx_pin_data *pin_data; + struct irq_domain *parent_domain; + struct device_node *parent_node; struct pinctrl_pin_desc *pins; struct pm8xxx_gpio *pctrl; - int ret; - int i, npins; + int ret, i; pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) return -ENOMEM; pctrl->dev = &pdev->dev; - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - pctrl->npins = npins; + pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!pctrl->regmap) { @@ -720,12 +808,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) for (i = 0; i < pctrl->desc.npins; i++) { pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); - pin_data[i].irq = platform_get_irq(pdev, i); - if (pin_data[i].irq < 0) { - dev_err(&pdev->dev, - "missing interrupts for pin %d\n", i); - return pin_data[i].irq; - } + pin_data[i].irq = -1; ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); if (ret) @@ -756,10 +839,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) pctrl->chip.of_gpio_n_cells = 2; pctrl->chip.label = dev_name(pctrl->dev); pctrl->chip.ngpio = pctrl->npins; + + parent_node = of_irq_find_parent(pctrl->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node); + pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0, + pctrl->chip.ngpio, + pctrl->fwnode, + &pm8xxx_domain_ops, + &pctrl->chip); + if (!pctrl->domain) + return -ENODEV; + ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { dev_err(&pdev->dev, "failed register gpiochip\n"); - return ret; + goto err_chip_add_data; } /* @@ -789,6 +891,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) unregister_gpiochip: gpiochip_remove(&pctrl->chip); +err_chip_add_data: + irq_domain_remove(pctrl->domain); return ret; } @@ -798,6 +902,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev) struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev); gpiochip_remove(&pctrl->chip); + irq_domain_remove(pctrl->domain); return 0; } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b5e9db85e881..a1ed13183559 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1303,6 +1303,20 @@ config HUAWEI_WMI To compile this driver as a module, choose M here: the module will be called huawei-wmi. +config PCENGINES_APU2 + tristate "PC Engines APUv2/3 front button and LEDs driver" + depends on INPUT && INPUT_KEYBOARD + depends on LEDS_CLASS + select GPIO_AMD_FCH + select KEYBOARD_GPIO_POLLED + select LEDS_GPIO + help + This driver provides support for the front button and LEDs on + PC Engines APUv2/APUv3 board. + + To compile this driver as a module, choose M here: the module + will be called pcengines-apuv2. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index ce8da260c223..86cb76677bc8 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -96,3 +96,4 @@ obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o +obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c new file mode 100644 index 000000000000..c1ca931e1fab --- /dev/null +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * PC-Engines APUv2/APUv3 board platform driver + * for gpio buttons and LEDs + * + * Copyright (C) 2018 metux IT consult + * Author: Enrico Weigelt <info@metux.net> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/dmi.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio_keys.h> +#include <linux/gpio/machine.h> +#include <linux/input.h> +#include <linux/platform_data/gpio/gpio-amd-fch.h> + +/* + * NOTE: this driver only supports APUv2/3 - not APUv1, as this one + * has completely different register layouts + */ + +/* register mappings */ +#define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57 +#define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58 +#define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1 +#define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1 +#define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2 + +/* order in which the gpio lines are defined in the register list */ +#define APU2_GPIO_LINE_LED1 0 +#define APU2_GPIO_LINE_LED2 1 +#define APU2_GPIO_LINE_LED3 2 +#define APU2_GPIO_LINE_MODESW 3 +#define APU2_GPIO_LINE_SIMSWAP 4 + +/* gpio device */ + +static int apu2_gpio_regs[] = { + [APU2_GPIO_LINE_LED1] = APU2_GPIO_REG_LED1, + [APU2_GPIO_LINE_LED2] = APU2_GPIO_REG_LED2, + [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3, + [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW, + [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP, +}; + +static const char * const apu2_gpio_names[] = { + [APU2_GPIO_LINE_LED1] = "front-led1", + [APU2_GPIO_LINE_LED2] = "front-led2", + [APU2_GPIO_LINE_LED3] = "front-led3", + [APU2_GPIO_LINE_MODESW] = "front-button", + [APU2_GPIO_LINE_SIMSWAP] = "simswap", +}; + +static const struct amd_fch_gpio_pdata board_apu2 = { + .gpio_num = ARRAY_SIZE(apu2_gpio_regs), + .gpio_reg = apu2_gpio_regs, + .gpio_names = apu2_gpio_names, +}; + +/* gpio leds device */ + +static const struct gpio_led apu2_leds[] = { + { .name = "apu:green:1" }, + { .name = "apu:green:2" }, + { .name = "apu:green:3" } +}; + +static const struct gpio_led_platform_data apu2_leds_pdata = { + .num_leds = ARRAY_SIZE(apu2_leds), + .leds = apu2_leds, +}; + +struct gpiod_lookup_table gpios_led_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1, + NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2, + NULL, 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3, + NULL, 2, GPIO_ACTIVE_LOW), + } +}; + +/* gpio keyboard device */ + +static struct gpio_keys_button apu2_keys_buttons[] = { + { + .code = KEY_SETUP, + .active_low = 1, + .desc = "front button", + .type = EV_KEY, + .debounce_interval = 10, + .value = 1, + }, +}; + +static const struct gpio_keys_platform_data apu2_keys_pdata = { + .buttons = apu2_keys_buttons, + .nbuttons = ARRAY_SIZE(apu2_keys_buttons), + .poll_interval = 100, + .rep = 0, + .name = "apu2-keys", +}; + +struct gpiod_lookup_table gpios_key_table = { + .dev_id = "gpio-keys-polled", + .table = { + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW, + NULL, 0, GPIO_ACTIVE_LOW), + } +}; + +/* board setup */ + +/* note: matching works on string prefix, so "apu2" must come before "apu" */ +static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { + + /* APU2 w/ legacy bios < 4.0.8 */ + { + .ident = "apu2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "APU2") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU2 w/ legacy bios >= 4.0.8 */ + { + .ident = "apu2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "apu2") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU2 w/ maainline bios */ + { + .ident = "apu2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2") + }, + .driver_data = (void *)&board_apu2, + }, + + /* APU3 w/ legacy bios < 4.0.8 */ + { + .ident = "apu3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "APU3") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU3 w/ legacy bios >= 4.0.8 */ + { + .ident = "apu3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "apu3") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU3 w/ mainline bios */ + { + .ident = "apu3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3") + }, + .driver_data = (void *)&board_apu2, + }, + {} +}; + +static struct platform_device *apu_gpio_pdev; +static struct platform_device *apu_leds_pdev; +static struct platform_device *apu_keys_pdev; + +static struct platform_device * __init apu_create_pdev( + const char *name, + const void *pdata, + size_t sz) +{ + struct platform_device *pdev; + + pdev = platform_device_register_resndata(NULL, + name, + PLATFORM_DEVID_NONE, + NULL, + 0, + pdata, + sz); + + if (IS_ERR(pdev)) + pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev)); + + return pdev; +} + +static int __init apu_board_init(void) +{ + const struct dmi_system_id *id; + + id = dmi_first_match(apu_gpio_dmi_table); + if (!id) { + pr_err("failed to detect apu board via dmi\n"); + return -ENODEV; + } + + gpiod_add_lookup_table(&gpios_led_table); + gpiod_add_lookup_table(&gpios_key_table); + + apu_gpio_pdev = apu_create_pdev( + AMD_FCH_GPIO_DRIVER_NAME, + id->driver_data, + sizeof(struct amd_fch_gpio_pdata)); + + apu_leds_pdev = apu_create_pdev( + "leds-gpio", + &apu2_leds_pdata, + sizeof(apu2_leds_pdata)); + + apu_keys_pdev = apu_create_pdev( + "gpio-keys-polled", + &apu2_keys_pdata, + sizeof(apu2_keys_pdata)); + + return 0; +} + +static void __exit apu_board_exit(void) +{ + gpiod_remove_lookup_table(&gpios_led_table); + gpiod_remove_lookup_table(&gpios_key_table); + + platform_device_unregister(apu_keys_pdev); + platform_device_unregister(apu_leds_pdev); + platform_device_unregister(apu_gpio_pdev); +} + +module_init(apu_board_init); +module_exit(apu_board_exit); + +MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); +MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LED/keys driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); +MODULE_ALIAS("platform:pcengines-apuv2"); +MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME); +MODULE_SOFTDEP("pre: platform:leds-gpio"); +MODULE_SOFTDEP("pre: platform:gpio_keys_polled"); diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 0d3b70b3bda8..d48ed7c2c6c4 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -12,7 +12,7 @@ if SPMI config SPMI_MSM_PMIC_ARB tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" - select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM default ARCH_QCOM diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 360b8218f322..928759242e42 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, return 0; } -static int qpnpint_irq_request_resources(struct irq_data *d) +static int qpnpint_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool reserve) { struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); u16 periph = hwirq_to_per(d->hwirq); @@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = { .irq_set_type = qpnpint_irq_set_type, .irq_set_wake = qpnpint_irq_set_wake, .irq_get_irqchip_state = qpnpint_get_irqchip_state, - .irq_request_resources = qpnpint_irq_request_resources, .flags = IRQCHIP_MASK_ON_SUSPEND, }; -static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, - unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) +static int qpnpint_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) { struct spmi_pmic_arb *pmic_arb = d->host_data; + u32 *intspec = fwspec->param; u16 apid, ppid; int rc; dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", intspec[0], intspec[1], intspec[2]); - if (irq_domain_get_of_node(d) != controller) + if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node) return -EINVAL; - if (intsize != 4) + if (fwspec->param_count != 4) return -EINVAL; if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) return -EINVAL; @@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, return 0; } -static int qpnpint_irq_domain_map(struct irq_domain *d, - unsigned int virq, - irq_hw_number_t hwirq) + +static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { - struct spmi_pmic_arb *pmic_arb = d->host_data; + irq_flow_handler_t handler; + + dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", + virq, hwirq, type); + + if (type & IRQ_TYPE_EDGE_BOTH) + handler = handle_edge_irq; + else + handler = handle_level_irq; + + irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, + handler, NULL, NULL); +} + +static int qpnpint_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + struct spmi_pmic_arb *pmic_arb = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; - dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); + for (i = 0; i < nr_irqs; i++) + qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, + type); - irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); - irq_set_chip_data(virq, d->host_data); - irq_set_noprobe(virq); return 0; } @@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { - .map = qpnpint_irq_domain_map, - .xlate = qpnpint_irq_domain_dt_translate, + .activate = qpnpint_irq_domain_activate, + .alloc = qpnpint_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = qpnpint_irq_domain_translate, }; static int spmi_pmic_arb_probe(struct platform_device *pdev) |