gpio: updates for v5.2 (part 1)

- batch of improvements for the vf610 driver which shrink the code and
   make use of resource managed helpers
 - support for a new variant of pca953x
 - make gpio-mockup buildable on systems without IOMEM
 - make gpio-74x164 more flexible by using generic device properties
   plus minor improvements
 - new driver for Mellanox BlueField
 - fixes for wakeup GPIOs in gpio-omap
 - use devm_platform_ioremap_resource() in gpio-mxc
 - a couple improvements of kernel docs for ACPI code
 - don't WARN() in gpiod_put() on optional GPIOs
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAlyrR7kACgkQEacuoBRx
 13KSJg/+NCSqM0OBMylRdNwfwQvpHj7Fp/6IRVVSpIetaVafPcPJ7TJtHeSBCE9X
 F9Cqpe9UlqLuBI+LXIIYLdsltirmhFfAUkhQvpFlUj4w9YyKojbISCgswUPMwFJ8
 JM6uiZaCHrHD7Q6m0znwmOJsCm/OOMhrjPWFrRTZNZOGl86eB/FifPvlWW/+GIkv
 RutHf+qiC7TkIpZO/mn4RIKwCOamFZvqUGTxasBU3+xwFrVlRW1ER8oWvSUjI8io
 OLNY25lgr8zfVCeBL9RDGTrS2R6HDt3ibkNO1mZfsADIKQxhl/U9UopIupCKA1we
 4tID5qLLm320vTVS+kUmrroKL7mNc7u9J7fo7AcrR03dn9697bBogrxTobCuw39r
 Alpm1SjsftfWyEIyNC+w+pvB7l8iLxFa7WeTTW4W+JSmrOMGp8xuCGAo1jNm/2CL
 ljs4BXYvhDgVMQwvgFm9B3r7Lyo4K83pbnScu2BGhWvmotXxQ37jL8GRM6EZFY3E
 rcZOiqqE6GlUXuO0hwiBqJsMzRLBHsHlX7yS7sajOx3tTyIOhLkDGt20YVsj1gVP
 I0438QZ4MeAiWnIpxMDjntPxPX92TwAP90sYX9ix0bJ12NbIQb+105TKYJnHj2eL
 LEEVrN4AUkSA7WFXPEAAxKEG9Z0dhagDtxsW+ZNwS4SvLEVl/Kc=
 =mSS+
 -----END PGP SIGNATURE-----

Merge tag 'gpio-v5.2-updates-for-linus-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux into devel

gpio: updates for v5.2 (part 1)

- batch of improvements for the vf610 driver which shrink the code and
  make use of resource managed helpers
- support for a new variant of pca953x
- make gpio-mockup buildable on systems without IOMEM
- make gpio-74x164 more flexible by using generic device properties
  plus minor improvements
- new driver for Mellanox BlueField
- fixes for wakeup GPIOs in gpio-omap
- use devm_platform_ioremap_resource() in gpio-mxc
- a couple improvements of kernel docs for ACPI code
- don't WARN() in gpiod_put() on optional GPIOs
This commit is contained in:
Linus Walleij 2019-04-08 15:48:11 +02:00
commit 4779a066e7
10 changed files with 279 additions and 125 deletions

View File

@ -30,6 +30,7 @@ Required properties:
ti,tca6424 ti,tca6424
ti,tca9539 ti,tca9539
ti,tca9554 ti,tca9554
onnn,cat9554
onnn,pca9654 onnn,pca9654
exar,xra1202 exar,xra1202
- gpio-controller: if used as gpio expander. - gpio-controller: if used as gpio expander.

View File

@ -330,20 +330,6 @@ config GPIO_MM_LANTIQ
(EBU) found on Lantiq SoCs. The gpios are output only as they are (EBU) found on Lantiq SoCs. The gpios are output only as they are
created by attaching a 16bit latch to the bus. created by attaching a 16bit latch to the bus.
config GPIO_MOCKUP
tristate "GPIO Testing Driver"
depends on GPIOLIB && SYSFS
select GPIO_SYSFS
select GPIOLIB_IRQCHIP
select IRQ_SIM
help
This enables GPIO Testing driver, which provides a way to test GPIO
subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS
must be selected for this test.
User could use it through the script in
tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in
it.
config GPIO_MPC5200 config GPIO_MPC5200
def_bool y def_bool y
depends on PPC_MPC52xx depends on PPC_MPC52xx
@ -1316,6 +1302,13 @@ config GPIO_MERRIFIELD
help help
Say Y here to support Intel Merrifield GPIO. Say Y here to support Intel Merrifield GPIO.
config GPIO_MLXBF
tristate "Mellanox BlueField SoC GPIO"
depends on (MELLANOX_PLATFORM && ARM64 && ACPI) || (64BIT && COMPILE_TEST)
select GPIO_GENERIC
help
Say Y here if you want GPIO support on Mellanox BlueField SoC.
config GPIO_ML_IOH config GPIO_ML_IOH
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support" tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
depends on X86 || COMPILE_TEST depends on X86 || COMPILE_TEST
@ -1442,4 +1435,16 @@ config GPIO_VIPERBOARD
endmenu endmenu
config GPIO_MOCKUP
tristate "GPIO Testing Driver"
depends on GPIOLIB
select IRQ_SIM
help
This enables GPIO Testing driver, which provides a way to test GPIO
subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS
must be selected for this test.
User could use it through the script in
tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in
it.
endif endif

View File

@ -85,6 +85,7 @@ obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o

View File

@ -1,21 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
* *
* Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com> * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/spi/spi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/slab.h> #include <linux/gpio/driver.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#define GEN_74X164_NUMBER_GPIOS 8 #define GEN_74X164_NUMBER_GPIOS 8
@ -116,10 +113,9 @@ static int gen_74x164_probe(struct spi_device *spi)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (of_property_read_u32(spi->dev.of_node, "registers-number", ret = device_property_read_u32(&spi->dev, "registers-number", &nregs);
&nregs)) { if (ret) {
dev_err(&spi->dev, dev_err(&spi->dev, "Missing 'registers-number' property.\n");
"Missing registers-number property in the DT.\n");
return -EINVAL; return -EINVAL;
} }

152
drivers/gpio/gpio-mlxbf.c Normal file
View File

@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/resource.h>
#include <linux/types.h>
/* Number of pins on BlueField */
#define MLXBF_GPIO_NR 54
/* Pad Electrical Controls. */
#define MLXBF_GPIO_PAD_CONTROL_FIRST_WORD 0x0700
#define MLXBF_GPIO_PAD_CONTROL_1_FIRST_WORD 0x0708
#define MLXBF_GPIO_PAD_CONTROL_2_FIRST_WORD 0x0710
#define MLXBF_GPIO_PAD_CONTROL_3_FIRST_WORD 0x0718
#define MLXBF_GPIO_PIN_DIR_I 0x1040
#define MLXBF_GPIO_PIN_DIR_O 0x1048
#define MLXBF_GPIO_PIN_STATE 0x1000
#define MLXBF_GPIO_SCRATCHPAD 0x20
#ifdef CONFIG_PM
struct mlxbf_gpio_context_save_regs {
u64 scratchpad;
u64 pad_control[MLXBF_GPIO_NR];
u64 pin_dir_i;
u64 pin_dir_o;
};
#endif
/* Device state structure. */
struct mlxbf_gpio_state {
struct gpio_chip gc;
/* Memory Address */
void __iomem *base;
#ifdef CONFIG_PM
struct mlxbf_gpio_context_save_regs csave_regs;
#endif
};
static int mlxbf_gpio_probe(struct platform_device *pdev)
{
struct mlxbf_gpio_state *gs;
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
int ret;
gs = devm_kzalloc(&pdev->dev, sizeof(*gs), GFP_KERNEL);
if (!gs)
return -ENOMEM;
gs->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gs->base))
return PTR_ERR(gs->base);
gc = &gs->gc;
ret = bgpio_init(gc, dev, 8,
gs->base + MLXBF_GPIO_PIN_STATE,
NULL,
NULL,
gs->base + MLXBF_GPIO_PIN_DIR_O,
gs->base + MLXBF_GPIO_PIN_DIR_I,
0);
if (ret)
return -ENODEV;
gc->owner = THIS_MODULE;
gc->ngpio = MLXBF_GPIO_NR;
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
if (ret) {
dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");
return ret;
}
platform_set_drvdata(pdev, gs);
dev_info(&pdev->dev, "registered Mellanox BlueField GPIO");
return 0;
}
#ifdef CONFIG_PM
static int mlxbf_gpio_suspend(struct platform_device *pdev, pm_message_t state)
{
struct mlxbf_gpio_state *gs = platform_get_drvdata(pdev);
gs->csave_regs.scratchpad = readq(gs->base + MLXBF_GPIO_SCRATCHPAD);
gs->csave_regs.pad_control[0] =
readq(gs->base + MLXBF_GPIO_PAD_CONTROL_FIRST_WORD);
gs->csave_regs.pad_control[1] =
readq(gs->base + MLXBF_GPIO_PAD_CONTROL_1_FIRST_WORD);
gs->csave_regs.pad_control[2] =
readq(gs->base + MLXBF_GPIO_PAD_CONTROL_2_FIRST_WORD);
gs->csave_regs.pad_control[3] =
readq(gs->base + MLXBF_GPIO_PAD_CONTROL_3_FIRST_WORD);
gs->csave_regs.pin_dir_i = readq(gs->base + MLXBF_GPIO_PIN_DIR_I);
gs->csave_regs.pin_dir_o = readq(gs->base + MLXBF_GPIO_PIN_DIR_O);
return 0;
}
static int mlxbf_gpio_resume(struct platform_device *pdev)
{
struct mlxbf_gpio_state *gs = platform_get_drvdata(pdev);
writeq(gs->csave_regs.scratchpad, gs->base + MLXBF_GPIO_SCRATCHPAD);
writeq(gs->csave_regs.pad_control[0],
gs->base + MLXBF_GPIO_PAD_CONTROL_FIRST_WORD);
writeq(gs->csave_regs.pad_control[1],
gs->base + MLXBF_GPIO_PAD_CONTROL_1_FIRST_WORD);
writeq(gs->csave_regs.pad_control[2],
gs->base + MLXBF_GPIO_PAD_CONTROL_2_FIRST_WORD);
writeq(gs->csave_regs.pad_control[3],
gs->base + MLXBF_GPIO_PAD_CONTROL_3_FIRST_WORD);
writeq(gs->csave_regs.pin_dir_i, gs->base + MLXBF_GPIO_PIN_DIR_I);
writeq(gs->csave_regs.pin_dir_o, gs->base + MLXBF_GPIO_PIN_DIR_O);
return 0;
}
#endif
static const struct acpi_device_id mlxbf_gpio_acpi_match[] = {
{ "MLNXBF02", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, mlxbf_gpio_acpi_match);
static struct platform_driver mlxbf_gpio_driver = {
.driver = {
.name = "mlxbf_gpio",
.acpi_match_table = ACPI_PTR(mlxbf_gpio_acpi_match),
},
.probe = mlxbf_gpio_probe,
#ifdef CONFIG_PM
.suspend = mlxbf_gpio_suspend,
.resume = mlxbf_gpio_resume,
#endif
};
module_platform_driver(mlxbf_gpio_driver);
MODULE_DESCRIPTION("Mellanox BlueField GPIO Driver");
MODULE_AUTHOR("Mellanox Technologies");
MODULE_LICENSE("GPL");

View File

@ -83,7 +83,6 @@ struct gpio_bank {
int stride; int stride;
u32 width; u32 width;
int context_loss_count; int context_loss_count;
bool workaround_enabled;
u32 quirks; u32 quirks;
void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable); void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
@ -353,6 +352,22 @@ static void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned offset)
} }
} }
/*
* Off mode wake-up capable GPIOs in bank(s) that are in the wakeup domain.
* See TRM section for GPIO for "Wake-Up Generation" for the list of GPIOs
* in wakeup domain. If bank->non_wakeup_gpios is not configured, assume none
* are capable waking up the system from off mode.
*/
static bool omap_gpio_is_off_wakeup_capable(struct gpio_bank *bank, u32 gpio_mask)
{
u32 no_wake = bank->non_wakeup_gpios;
if (no_wake)
return !!(~no_wake & gpio_mask);
return false;
}
static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio, static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
unsigned trigger) unsigned trigger)
{ {
@ -384,13 +399,7 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
} }
/* This part needs to be executed always for OMAP{34xx, 44xx} */ /* This part needs to be executed always for OMAP{34xx, 44xx} */
if (!bank->regs->irqctrl) { if (!bank->regs->irqctrl && !omap_gpio_is_off_wakeup_capable(bank, gpio)) {
/* On omap24xx proceed only when valid GPIO bit is set */
if (bank->non_wakeup_gpios) {
if (!(bank->non_wakeup_gpios & gpio_bit))
goto exit;
}
/* /*
* Log the edge gpio and manually trigger the IRQ * Log the edge gpio and manually trigger the IRQ
* after resume if the input level changes * after resume if the input level changes
@ -403,7 +412,6 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
bank->enabled_non_wakeup_gpios &= ~gpio_bit; bank->enabled_non_wakeup_gpios &= ~gpio_bit;
} }
exit:
bank->level_mask = bank->level_mask =
readl_relaxed(bank->base + bank->regs->leveldetect0) | readl_relaxed(bank->base + bank->regs->leveldetect0) |
readl_relaxed(bank->base + bank->regs->leveldetect1); readl_relaxed(bank->base + bank->regs->leveldetect1);
@ -1311,7 +1319,10 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context) static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
{ {
struct device *dev = bank->chip.parent; struct device *dev = bank->chip.parent;
u32 l1 = 0, l2 = 0; void __iomem *base = bank->base;
u32 nowake;
bank->saved_datain = readl_relaxed(base + bank->regs->datain);
if (bank->funcs.idle_enable_level_quirk) if (bank->funcs.idle_enable_level_quirk)
bank->funcs.idle_enable_level_quirk(bank); bank->funcs.idle_enable_level_quirk(bank);
@ -1323,22 +1334,15 @@ static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
goto update_gpio_context_count; goto update_gpio_context_count;
/* /*
* If going to OFF, remove triggering for all * If going to OFF, remove triggering for all wkup domain
* non-wakeup GPIOs. Otherwise spurious IRQs will be * non-wakeup GPIOs. Otherwise spurious IRQs will be
* generated. See OMAP2420 Errata item 1.101. * generated. See OMAP2420 Errata item 1.101.
*/ */
bank->saved_datain = readl_relaxed(bank->base + if (!bank->loses_context && bank->enabled_non_wakeup_gpios) {
bank->regs->datain); nowake = bank->enabled_non_wakeup_gpios;
l1 = bank->context.fallingdetect; omap_gpio_rmw(base, bank->regs->fallingdetect, nowake, ~nowake);
l2 = bank->context.risingdetect; omap_gpio_rmw(base, bank->regs->risingdetect, nowake, ~nowake);
}
l1 &= ~bank->enabled_non_wakeup_gpios;
l2 &= ~bank->enabled_non_wakeup_gpios;
writel_relaxed(l1, bank->base + bank->regs->fallingdetect);
writel_relaxed(l2, bank->base + bank->regs->risingdetect);
bank->workaround_enabled = true;
update_gpio_context_count: update_gpio_context_count:
if (bank->get_context_loss_count) if (bank->get_context_loss_count)
@ -1383,11 +1387,14 @@ static void omap_gpio_unidle(struct gpio_bank *bank)
return; return;
} }
} }
} else {
/* Restore changes done for OMAP2420 errata 1.101 */
writel_relaxed(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
writel_relaxed(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
} }
if (!bank->workaround_enabled)
return;
l = readl_relaxed(bank->base + bank->regs->datain); l = readl_relaxed(bank->base + bank->regs->datain);
/* /*
@ -1437,8 +1444,6 @@ static void omap_gpio_unidle(struct gpio_bank *bank)
writel_relaxed(old0, bank->base + bank->regs->leveldetect0); writel_relaxed(old0, bank->base + bank->regs->leveldetect0);
writel_relaxed(old1, bank->base + bank->regs->leveldetect1); writel_relaxed(old1, bank->base + bank->regs->leveldetect1);
} }
bank->workaround_enabled = false;
} }
static int gpio_omap_cpu_notifier(struct notifier_block *nb, static int gpio_omap_cpu_notifier(struct notifier_block *nb,

View File

@ -1178,6 +1178,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
{ .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), }, { .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "exar,xra1202", .data = OF_953X( 8, 0), }, { .compatible = "exar,xra1202", .data = OF_953X( 8, 0), },

View File

@ -29,6 +29,7 @@ struct fsl_gpio_soc_data {
struct vf610_gpio_port { struct vf610_gpio_port {
struct gpio_chip gc; struct gpio_chip gc;
struct irq_chip ic;
void __iomem *base; void __iomem *base;
void __iomem *gpio_base; void __iomem *gpio_base;
const struct fsl_gpio_soc_data *sdata; const struct fsl_gpio_soc_data *sdata;
@ -60,8 +61,6 @@ struct vf610_gpio_port {
#define PORT_INT_EITHER_EDGE 0xb #define PORT_INT_EITHER_EDGE 0xb
#define PORT_INT_LOGIC_ONE 0xc #define PORT_INT_LOGIC_ONE 0xc
static struct irq_chip vf610_gpio_irq_chip;
static const struct fsl_gpio_soc_data imx_data = { static const struct fsl_gpio_soc_data imx_data = {
.have_paddr = true, .have_paddr = true,
}; };
@ -86,28 +85,24 @@ static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{ {
struct vf610_gpio_port *port = gpiochip_get_data(gc); struct vf610_gpio_port *port = gpiochip_get_data(gc);
unsigned long mask = BIT(gpio); unsigned long mask = BIT(gpio);
void __iomem *addr; unsigned long offset = GPIO_PDIR;
if (port->sdata && port->sdata->have_paddr) { if (port->sdata && port->sdata->have_paddr) {
mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR); mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
addr = mask ? port->gpio_base + GPIO_PDOR : if (mask)
port->gpio_base + GPIO_PDIR; offset = GPIO_PDOR;
return !!(vf610_gpio_readl(addr) & BIT(gpio));
} else {
return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR)
& BIT(gpio));
} }
return !!(vf610_gpio_readl(port->gpio_base + offset) & BIT(gpio));
} }
static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{ {
struct vf610_gpio_port *port = gpiochip_get_data(gc); struct vf610_gpio_port *port = gpiochip_get_data(gc);
unsigned long mask = BIT(gpio); unsigned long mask = BIT(gpio);
unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR;
if (val) vf610_gpio_writel(mask, port->gpio_base + offset);
vf610_gpio_writel(mask, port->gpio_base + GPIO_PSOR);
else
vf610_gpio_writel(mask, port->gpio_base + GPIO_PCOR);
} }
static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
@ -237,14 +232,10 @@ static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
return 0; return 0;
} }
static struct irq_chip vf610_gpio_irq_chip = { static void vf610_gpio_disable_clk(void *data)
.name = "gpio-vf610", {
.irq_ack = vf610_gpio_irq_ack, clk_disable_unprepare(data);
.irq_mask = vf610_gpio_irq_mask, }
.irq_unmask = vf610_gpio_irq_unmask,
.irq_set_type = vf610_gpio_irq_set_type,
.irq_set_wake = vf610_gpio_irq_set_wake,
};
static int vf610_gpio_probe(struct platform_device *pdev) static int vf610_gpio_probe(struct platform_device *pdev)
{ {
@ -252,10 +243,11 @@ static int vf610_gpio_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct vf610_gpio_port *port; struct vf610_gpio_port *port;
struct gpio_chip *gc; struct gpio_chip *gc;
struct irq_chip *ic;
int i; int i;
int ret; int ret;
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port) if (!port)
return -ENOMEM; return -ENOMEM;
@ -272,11 +264,15 @@ static int vf610_gpio_probe(struct platform_device *pdev)
if (port->irq < 0) if (port->irq < 0)
return port->irq; return port->irq;
port->clk_port = devm_clk_get(&pdev->dev, "port"); port->clk_port = devm_clk_get(dev, "port");
if (!IS_ERR(port->clk_port)) { if (!IS_ERR(port->clk_port)) {
ret = clk_prepare_enable(port->clk_port); ret = clk_prepare_enable(port->clk_port);
if (ret) if (ret)
return ret; return ret;
ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,
port->clk_port);
if (ret)
return ret;
} else if (port->clk_port == ERR_PTR(-EPROBE_DEFER)) { } else if (port->clk_port == ERR_PTR(-EPROBE_DEFER)) {
/* /*
* Percolate deferrals, for anything else, * Percolate deferrals, for anything else,
@ -285,20 +281,19 @@ static int vf610_gpio_probe(struct platform_device *pdev)
return PTR_ERR(port->clk_port); return PTR_ERR(port->clk_port);
} }
port->clk_gpio = devm_clk_get(&pdev->dev, "gpio"); port->clk_gpio = devm_clk_get(dev, "gpio");
if (!IS_ERR(port->clk_gpio)) { if (!IS_ERR(port->clk_gpio)) {
ret = clk_prepare_enable(port->clk_gpio); ret = clk_prepare_enable(port->clk_gpio);
if (ret) { if (ret)
clk_disable_unprepare(port->clk_port); return ret;
ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,
port->clk_gpio);
if (ret)
return ret; return ret;
}
} else if (port->clk_gpio == ERR_PTR(-EPROBE_DEFER)) { } else if (port->clk_gpio == ERR_PTR(-EPROBE_DEFER)) {
clk_disable_unprepare(port->clk_port);
return PTR_ERR(port->clk_gpio); return PTR_ERR(port->clk_gpio);
} }
platform_set_drvdata(pdev, port);
gc = &port->gc; gc = &port->gc;
gc->of_node = np; gc->of_node = np;
gc->parent = dev; gc->parent = dev;
@ -313,7 +308,15 @@ static int vf610_gpio_probe(struct platform_device *pdev)
gc->direction_output = vf610_gpio_direction_output; gc->direction_output = vf610_gpio_direction_output;
gc->set = vf610_gpio_set; gc->set = vf610_gpio_set;
ret = gpiochip_add_data(gc, port); ic = &port->ic;
ic->name = "gpio-vf610";
ic->irq_ack = vf610_gpio_irq_ack;
ic->irq_mask = vf610_gpio_irq_mask;
ic->irq_unmask = vf610_gpio_irq_unmask;
ic->irq_set_type = vf610_gpio_irq_set_type;
ic->irq_set_wake = vf610_gpio_irq_set_wake;
ret = devm_gpiochip_add_data(dev, gc, port);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -324,39 +327,23 @@ static int vf610_gpio_probe(struct platform_device *pdev)
/* Clear the interrupt status register for all GPIO's */ /* Clear the interrupt status register for all GPIO's */
vf610_gpio_writel(~0, port->base + PORT_ISFR); vf610_gpio_writel(~0, port->base + PORT_ISFR);
ret = gpiochip_irqchip_add(gc, &vf610_gpio_irq_chip, 0, ret = gpiochip_irqchip_add(gc, ic, 0, handle_edge_irq, IRQ_TYPE_NONE);
handle_edge_irq, IRQ_TYPE_NONE);
if (ret) { if (ret) {
dev_err(dev, "failed to add irqchip\n"); dev_err(dev, "failed to add irqchip\n");
gpiochip_remove(gc);
return ret; return ret;
} }
gpiochip_set_chained_irqchip(gc, &vf610_gpio_irq_chip, port->irq, gpiochip_set_chained_irqchip(gc, ic, port->irq,
vf610_gpio_irq_handler); vf610_gpio_irq_handler);
return 0; return 0;
} }
static int vf610_gpio_remove(struct platform_device *pdev)
{
struct vf610_gpio_port *port = platform_get_drvdata(pdev);
gpiochip_remove(&port->gc);
if (!IS_ERR(port->clk_port))
clk_disable_unprepare(port->clk_port);
if (!IS_ERR(port->clk_gpio))
clk_disable_unprepare(port->clk_gpio);
return 0;
}
static struct platform_driver vf610_gpio_driver = { static struct platform_driver vf610_gpio_driver = {
.driver = { .driver = {
.name = "gpio-vf610", .name = "gpio-vf610",
.of_match_table = vf610_gpio_dt_ids, .of_match_table = vf610_gpio_dt_ids,
}, },
.probe = vf610_gpio_probe, .probe = vf610_gpio_probe,
.remove = vf610_gpio_remove,
}; };
builtin_platform_driver(vf610_gpio_driver); builtin_platform_driver(vf610_gpio_driver);

View File

@ -24,13 +24,13 @@
* *
* @node: list-entry of the events list of the struct acpi_gpio_chip * @node: list-entry of the events list of the struct acpi_gpio_chip
* @handle: handle of ACPI method to execute when the IRQ triggers * @handle: handle of ACPI method to execute when the IRQ triggers
* @handler: irq_handler to pass to request_irq when requesting the IRQ * @handler: handler function to pass to request_irq() when requesting the IRQ
* @pin: GPIO pin number on the gpio_chip * @pin: GPIO pin number on the struct gpio_chip
* @irq: Linux IRQ number for the event, for request_ / free_irq * @irq: Linux IRQ number for the event, for request_irq() / free_irq()
* @irqflags: flags to pass to request_irq when requesting the 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 * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source
* @irq_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 * @desc: struct gpio_desc for the GPIO pin for this event
*/ */
struct acpi_gpio_event { struct acpi_gpio_event {
struct list_head node; struct list_head node;
@ -65,10 +65,10 @@ struct acpi_gpio_chip {
}; };
/* /*
* For gpiochips which call acpi_gpiochip_request_interrupts() before late_init * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init
* (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a
* late_initcall_sync handler, so that other builtin drivers can register their * late_initcall_sync() handler, so that other builtin drivers can register their
* OpRegions before the event handlers can run. This list contains gpiochips * OpRegions before the event handlers can run. This list contains GPIO chips
* for which the acpi_gpiochip_request_irqs() call has been deferred. * for which the acpi_gpiochip_request_irqs() call has been deferred.
*/ */
static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock);
@ -90,7 +90,7 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
* *
* Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
* error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO
* controller does not have gpiochip registered at the moment. This is to * controller does not have GPIO chip registered at the moment. This is to
* support probe deferral. * support probe deferral.
*/ */
static struct gpio_desc *acpi_get_gpiod(char *path, int pin) static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
@ -287,9 +287,9 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
* *
* ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are
* handled by ACPI event methods which need to be called from the GPIO * handled by ACPI event methods which need to be called from the GPIO
* chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which * chip's interrupt handler. acpi_gpiochip_request_interrupts() finds out which
* gpio pins have acpi event methods and assigns interrupt handlers that calls * GPIO pins have ACPI event methods and assigns interrupt handlers that calls
* the acpi event methods for those pins. * the ACPI event methods for those pins.
*/ */
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
{ {
@ -653,7 +653,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
* that case @index is used to select the GPIO entry in the property value * that case @index is used to select the GPIO entry in the property value
* (in case of multiple). * (in case of multiple).
* *
* If the GPIO cannot be translated or there is an error an ERR_PTR is * If the GPIO cannot be translated or there is an error, an ERR_PTR is
* returned. * returned.
* *
* Note: if the GPIO resource has multiple entries in the pin list, this * Note: if the GPIO resource has multiple entries in the pin list, this
@ -751,10 +751,13 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
* @index: index of GpioIo/GpioInt resource (starting from %0) * @index: index of GpioIo/GpioInt resource (starting from %0)
* @info: info pointer to fill in (optional) * @info: info pointer to fill in (optional)
* *
* If @fwnode is an ACPI device object, call %acpi_get_gpiod_by_index() for it. * If @fwnode is an ACPI device object, call acpi_get_gpiod_by_index() for it.
* Otherwise (ie. it is a data-only non-device object), use the property-based * Otherwise (i.e. it is a data-only non-device object), use the property-based
* GPIO lookup to get to the GPIO resource with the relevant information and use * GPIO lookup to get to the GPIO resource with the relevant information and use
* that to obtain the GPIO descriptor to return. * that to obtain the GPIO descriptor to return.
*
* If the GPIO cannot be translated or there is an error an ERR_PTR is
* returned.
*/ */
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index, const char *propname, int index,
@ -1158,11 +1161,13 @@ static int acpi_find_gpio_count(struct acpi_resource *ares, void *data)
} }
/** /**
* acpi_gpio_count - return the number of GPIOs associated with a * acpi_gpio_count - count the GPIOs associated with a device / function
* device / function or -ENOENT if no GPIO has been * @dev: GPIO consumer, can be %NULL for system-global GPIOs
* assigned to the requested function.
* @dev: GPIO consumer, can be NULL for system-global GPIOs
* @con_id: function within the GPIO consumer * @con_id: function within the GPIO consumer
*
* Return:
* The number of GPIOs associated with a device / function or %-ENOENT,
* if no GPIO has been assigned to the requested function.
*/ */
int acpi_gpio_count(struct device *dev, const char *con_id) int acpi_gpio_count(struct device *dev, const char *con_id)
{ {

View File

@ -4626,7 +4626,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional);
*/ */
void gpiod_put(struct gpio_desc *desc) void gpiod_put(struct gpio_desc *desc)
{ {
gpiod_free(desc); if (desc)
gpiod_free(desc);
} }
EXPORT_SYMBOL_GPL(gpiod_put); EXPORT_SYMBOL_GPL(gpiod_put);