diff options
Diffstat (limited to 'drivers/gpio')
35 files changed, 843 insertions, 246 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5521f060d58e..e382dfebad7c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -704,18 +704,6 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. -config GPIO_VX855 - tristate "VIA VX855/VX875 GPIO" - depends on (X86 || COMPILE_TEST) && PCI - select MFD_CORE - select MFD_VX855 - help - Support access to the VX855/VX875 GPIO lines through the GPIO library. - - This driver provides common support for accessing the device. - Additional drivers must be enabled in order to use the - functionality of the device. - config GPIO_WCD934X tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X && OF_GPIO @@ -835,7 +823,19 @@ config GPIO_IDT3243X endmenu menu "Port-mapped I/O GPIO drivers" - depends on X86 # Unconditional I/O space access + depends on X86 && HAS_IOPORT # I/O space access + +config GPIO_VX855 + tristate "VIA VX855/VX875 GPIO" + depends on PCI + select MFD_CORE + select MFD_VX855 + help + Support access to the VX855/VX875 GPIO lines through the GPIO library. + + This driver provides common support for accessing the device. + Additional drivers must be enabled in order to use the + functionality of the device. config GPIO_I8255 tristate @@ -897,7 +897,7 @@ config GPIO_F7188X help This option enables support for GPIOs found on Fintek Super-I/O chips F71869, F71869A, F71882FG, F71889F and F81866. - As well as Nuvoton Super-I/O chip NCT6116D. + As well as Nuvoton Super-I/O chip NCT6126D. To compile this driver as a module, choose M here: the module will be called f7188x-gpio. @@ -1440,6 +1440,22 @@ config GPIO_TPS65218 Select this option to enable GPIO driver for the TPS65218 chip family. +config GPIO_TPS65219 + tristate "TPS65219 GPIO" + depends on MFD_TPS65219 + default MFD_TPS65219 + help + Select this option to enable GPIO driver for the TPS65219 chip + family. + GPIO0 is statically configured as either input or output prior to + Linux boot. It is used for MULTI_DEVICE_ENABLE function. This setting + is statically configured by NVM. GPIO0 can't be used as a generic + GPIO. It's either a GPO when MULTI_DEVICE_EN=0 or a GPI when + MULTI_DEVICE_EN=1. + + This driver can also be built as a module. If so, the module will be + called gpio_tps65219. + config GPIO_TPS6586X bool "TPS6586X GPIO" depends on MFD_TPS6586X @@ -1583,6 +1599,19 @@ config GPIO_MLXBF2 help Say Y here if you want GPIO support on Mellanox BlueField 2 SoC. +config GPIO_MLXBF3 + tristate "Mellanox BlueField 3 SoC GPIO" + depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y if you want GPIO support on Mellanox BlueField 3 SoC. + This GPIO controller supports interrupt handling and enables the + manipulation of certain GPIO pins. + This controller should be used in parallel with pinctrl-mlxbf3 to + control the desired GPIOs. + This driver can also be built as a module called mlxbf3-gpio. + config GPIO_ML_IOH tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support" depends on X86 || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 20036af3acb1..c3ac51d47aa9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -104,6 +104,7 @@ obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o obj-$(CONFIG_GPIO_MLXBF2) += gpio-mlxbf2.o +obj-$(CONFIG_GPIO_MLXBF3) += gpio-mlxbf3.o obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o @@ -160,6 +161,7 @@ obj-$(CONFIG_GPIO_TN48M_CPLD) += gpio-tn48m.o obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o +obj-$(CONFIG_GPIO_TPS65219) += gpio-tps65219.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index f2253fd5ab4b..8ff5f4ff5958 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -100,13 +100,23 @@ static const struct regmap_irq dio48e_regmap_irqs[] = { DIO48E_REGMAP_IRQ(0), DIO48E_REGMAP_IRQ(1), }; -static int dio48e_handle_mask_sync(struct regmap *const map, const int index, +/** + * struct dio48e_gpio - GPIO device private data structure + * @map: Regmap for the device + * @irq_mask: Current IRQ mask state on the device + */ +struct dio48e_gpio { + struct regmap *map; + unsigned int irq_mask; +}; + +static int dio48e_handle_mask_sync(const int index, const unsigned int mask_buf_def, const unsigned int mask_buf, void *const irq_drv_data) { - unsigned int *const irq_mask = irq_drv_data; - const unsigned int prev_mask = *irq_mask; + struct dio48e_gpio *const dio48egpio = irq_drv_data; + const unsigned int prev_mask = dio48egpio->irq_mask; int err; unsigned int val; @@ -115,19 +125,19 @@ static int dio48e_handle_mask_sync(struct regmap *const map, const int index, return 0; /* remember the current mask for the next mask sync */ - *irq_mask = mask_buf; + dio48egpio->irq_mask = mask_buf; /* if all previously masked, enable interrupts when unmasking */ if (prev_mask == mask_buf_def) { - err = regmap_write(map, DIO48E_CLEAR_INTERRUPT, 0x00); + err = regmap_write(dio48egpio->map, DIO48E_CLEAR_INTERRUPT, 0x00); if (err) return err; - return regmap_write(map, DIO48E_ENABLE_INTERRUPT, 0x00); + return regmap_write(dio48egpio->map, DIO48E_ENABLE_INTERRUPT, 0x00); } /* if all are currently masked, disable interrupts */ if (mask_buf == mask_buf_def) - return regmap_read(map, DIO48E_DISABLE_INTERRUPT, &val); + return regmap_read(dio48egpio->map, DIO48E_DISABLE_INTERRUPT, &val); return 0; } @@ -168,7 +178,7 @@ static int dio48e_probe(struct device *dev, unsigned int id) struct regmap *map; int err; struct regmap_irq_chip *chip; - unsigned int irq_mask; + struct dio48e_gpio *dio48egpio; struct regmap_irq_chip_data *chip_data; if (!devm_request_region(dev, base[id], DIO48E_EXTENT, name)) { @@ -186,12 +196,14 @@ static int dio48e_probe(struct device *dev, unsigned int id) return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n"); - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); + if (!dio48egpio) return -ENOMEM; - chip->irq_drv_data = devm_kzalloc(dev, sizeof(irq_mask), GFP_KERNEL); - if (!chip->irq_drv_data) + dio48egpio->map = map; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) return -ENOMEM; chip->name = name; @@ -202,6 +214,7 @@ static int dio48e_probe(struct device *dev, unsigned int id) chip->irqs = dio48e_regmap_irqs; chip->num_irqs = ARRAY_SIZE(dio48e_regmap_irqs); chip->handle_mask_sync = dio48e_handle_mask_sync; + chip->irq_drv_data = dio48egpio; /* Initialize to prevent spurious interrupts before we're ready */ err = dio48e_irq_init_hw(map); diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 9b01c391efce..6dafab0cf964 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -535,7 +535,7 @@ static struct i2c_driver adnp_i2c_driver = { .name = "gpio-adnp", .of_match_table = adnp_of_match, }, - .probe_new = adnp_i2c_probe, + .probe = adnp_i2c_probe, .id_table = adnp_i2c_id, }; module_i2c_driver(adnp_i2c_driver); diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 20a686f12df7..38e0fff9afe7 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -10,12 +10,15 @@ #include <linux/bitmap.h> #include <linux/bitops.h> #include <linux/ctype.h> +#include <linux/delay.h> #include <linux/idr.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/overflow.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> @@ -239,6 +242,11 @@ static void __exit gpio_aggregator_remove_all(void) * GPIO Forwarder */ +struct gpiochip_fwd_timing { + u32 ramp_up_us; + u32 ramp_down_us; +}; + struct gpiochip_fwd { struct gpio_chip chip; struct gpio_desc **descs; @@ -246,6 +254,7 @@ struct gpiochip_fwd { struct mutex mlock; /* protects tmp[] if can_sleep */ spinlock_t slock; /* protects tmp[] if !can_sleep */ }; + struct gpiochip_fwd_timing *delay_timings; unsigned long tmp[]; /* values and descs for multiple ops */ }; @@ -330,6 +339,27 @@ static int gpio_fwd_get_multiple_locked(struct gpio_chip *chip, return error; } +static void gpio_fwd_delay(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + const struct gpiochip_fwd_timing *delay_timings; + bool is_active_low = gpiod_is_active_low(fwd->descs[offset]); + u32 delay_us; + + delay_timings = &fwd->delay_timings[offset]; + if ((!is_active_low && value) || (is_active_low && !value)) + delay_us = delay_timings->ramp_up_us; + else + delay_us = delay_timings->ramp_down_us; + if (!delay_us) + return; + + if (chip->can_sleep) + fsleep(delay_us); + else + udelay(delay_us); +} + static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiochip_fwd *fwd = gpiochip_get_data(chip); @@ -338,6 +368,9 @@ static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) gpiod_set_value_cansleep(fwd->descs[offset], value); else gpiod_set_value(fwd->descs[offset], value); + + if (fwd->delay_timings) + gpio_fwd_delay(chip, offset, value); } static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, @@ -390,6 +423,59 @@ static int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset) return gpiod_to_irq(fwd->descs[offset]); } +/* + * The GPIO delay provides a way to configure platform specific delays + * for the GPIO ramp-up or ramp-down delays. This can serve the following + * purposes: + * - Open-drain output using an RC filter + */ +#define FWD_FEATURE_DELAY BIT(0) + +#ifdef CONFIG_OF_GPIO +static int gpiochip_fwd_delay_of_xlate(struct gpio_chip *chip, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + struct gpiochip_fwd_timing *timings; + u32 line; + + if (gpiospec->args_count != chip->of_gpio_n_cells) + return -EINVAL; + + line = gpiospec->args[0]; + if (line >= chip->ngpio) + return -EINVAL; + + timings = &fwd->delay_timings[line]; + timings->ramp_up_us = gpiospec->args[1]; + timings->ramp_down_us = gpiospec->args[2]; + + return line; +} + +static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip, + struct gpiochip_fwd *fwd) +{ + fwd->delay_timings = devm_kcalloc(dev, chip->ngpio, + sizeof(*fwd->delay_timings), + GFP_KERNEL); + if (!fwd->delay_timings) + return -ENOMEM; + + chip->of_xlate = gpiochip_fwd_delay_of_xlate; + chip->of_gpio_n_cells = 3; + + return 0; +} +#else +static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip, + struct gpiochip_fwd *fwd) +{ + return 0; +} +#endif /* !CONFIG_OF_GPIO */ + /** * gpiochip_fwd_create() - Create a new GPIO forwarder * @dev: Parent device pointer @@ -397,6 +483,7 @@ static int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset) * @descs: Array containing the GPIO descriptors to forward to. * This array must contain @ngpios entries, and must not be deallocated * before the forwarder has been destroyed again. + * @features: Bitwise ORed features as defined with FWD_FEATURE_*. * * This function creates a new gpiochip, which forwards all GPIO operations to * the passed GPIO descriptors. @@ -406,7 +493,8 @@ static int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset) */ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, unsigned int ngpios, - struct gpio_desc *descs[]) + struct gpio_desc *descs[], + unsigned long features) { const char *label = dev_name(dev); struct gpiochip_fwd *fwd; @@ -459,6 +547,12 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, else spin_lock_init(&fwd->slock); + if (features & FWD_FEATURE_DELAY) { + error = gpiochip_fwd_setup_delay_line(dev, chip, fwd); + if (error) + return ERR_PTR(error); + } + error = devm_gpiochip_add_data(dev, chip, fwd); if (error) return ERR_PTR(error); @@ -476,6 +570,7 @@ static int gpio_aggregator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct gpio_desc **descs; struct gpiochip_fwd *fwd; + unsigned long features; int i, n; n = gpiod_count(dev, NULL); @@ -492,7 +587,8 @@ static int gpio_aggregator_probe(struct platform_device *pdev) return PTR_ERR(descs[i]); } - fwd = gpiochip_fwd_create(dev, n, descs); + features = (uintptr_t)device_get_match_data(dev); + fwd = gpiochip_fwd_create(dev, n, descs, features); if (IS_ERR(fwd)) return PTR_ERR(fwd); @@ -500,23 +596,25 @@ static int gpio_aggregator_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id gpio_aggregator_dt_ids[] = { + { + .compatible = "gpio-delay", + .data = (void *)FWD_FEATURE_DELAY, + }, /* * Add GPIO-operated devices controlled from userspace below, - * or use "driver_override" in sysfs + * or use "driver_override" in sysfs. */ {} }; MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids); -#endif static struct platform_driver gpio_aggregator_driver = { .probe = gpio_aggregator_probe, .driver = { .name = DRV_NAME, .groups = gpio_aggregator_groups, - .of_match_table = of_match_ptr(gpio_aggregator_dt_ids), + .of_match_table = gpio_aggregator_dt_ids, }, }; diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index c55b35da61a0..6566517fe0d8 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -609,8 +609,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); INIT_LIST_HEAD(&priv->bank_list); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg_base = devm_ioremap_resource(dev, res); + reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(reg_base)) return PTR_ERR(reg_base); diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index aaaf61dc2632..fff510d86e31 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -692,7 +692,7 @@ static int davinci_gpio_resume(struct device *dev) return 0; } -DEFINE_SIMPLE_DEV_PM_OPS(davinci_gpio_dev_pm_ops, davinci_gpio_suspend, +static DEFINE_SIMPLE_DEV_PM_OPS(davinci_gpio_dev_pm_ops, davinci_gpio_suspend, davinci_gpio_resume); static const struct of_device_id davinci_gpio_ids[] = { @@ -712,7 +712,7 @@ static struct platform_driver davinci_gpio_driver = { }, }; -/** +/* * GPIO driver registration needs to be done before machine_init functions * access GPIO. Hence davinci_gpio_drv_reg() is a postcore_initcall. */ diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 9effa7769bef..f54ca5a1775e 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -48,7 +48,7 @@ /* * Nuvoton devices. */ -#define SIO_NCT6116D_ID 0xD283 /* NCT6116D chipset ID */ +#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */ #define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */ @@ -62,7 +62,7 @@ enum chips { f81866, f81804, f81865, - nct6116d, + nct6126d, }; static const char * const f7188x_names[] = { @@ -74,7 +74,7 @@ static const char * const f7188x_names[] = { "f81866", "f81804", "f81865", - "nct6116d", + "nct6126d", }; struct f7188x_sio { @@ -187,8 +187,8 @@ static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, /* Output mode register (0:open drain 1:push-pull). */ #define f7188x_gpio_out_mode(base) ((base) + 3) -#define f7188x_gpio_dir_invert(type) ((type) == nct6116d) -#define f7188x_gpio_data_single(type) ((type) == nct6116d) +#define f7188x_gpio_dir_invert(type) ((type) == nct6126d) +#define f7188x_gpio_data_single(type) ((type) == nct6126d) static struct f7188x_gpio_bank f71869_gpio_bank[] = { F7188X_GPIO_BANK(0, 6, 0xF0, DRVNAME "-0"), @@ -274,7 +274,7 @@ static struct f7188x_gpio_bank f81865_gpio_bank[] = { F7188X_GPIO_BANK(60, 5, 0x90, DRVNAME "-6"), }; -static struct f7188x_gpio_bank nct6116d_gpio_bank[] = { +static struct f7188x_gpio_bank nct6126d_gpio_bank[] = { F7188X_GPIO_BANK(0, 8, 0xE0, DRVNAME "-0"), F7188X_GPIO_BANK(10, 8, 0xE4, DRVNAME "-1"), F7188X_GPIO_BANK(20, 8, 0xE8, DRVNAME "-2"), @@ -282,7 +282,7 @@ static struct f7188x_gpio_bank nct6116d_gpio_bank[] = { F7188X_GPIO_BANK(40, 8, 0xF0, DRVNAME "-4"), F7188X_GPIO_BANK(50, 8, 0xF4, DRVNAME "-5"), F7188X_GPIO_BANK(60, 8, 0xF8, DRVNAME "-6"), - F7188X_GPIO_BANK(70, 1, 0xFC, DRVNAME "-7"), + F7188X_GPIO_BANK(70, 8, 0xFC, DRVNAME "-7"), }; static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -490,9 +490,9 @@ static int f7188x_gpio_probe(struct platform_device *pdev) data->nr_bank = ARRAY_SIZE(f81865_gpio_bank); data->bank = f81865_gpio_bank; break; - case nct6116d: - data->nr_bank = ARRAY_SIZE(nct6116d_gpio_bank); - data->bank = nct6116d_gpio_bank; + case nct6126d: + data->nr_bank = ARRAY_SIZE(nct6126d_gpio_bank); + data->bank = nct6126d_gpio_bank; break; default: return -ENODEV; @@ -559,9 +559,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81865_ID: sio->type = f81865; break; - case SIO_NCT6116D_ID: + case SIO_NCT6126D_ID: sio->device = SIO_LD_GPIO_NUVOTON; - sio->type = nct6116d; + sio->type = nct6126d; break; default: pr_info("Unsupported Fintek device 0x%04x\n", devid); @@ -569,7 +569,7 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) } /* double check manufacturer where possible */ - if (sio->type != nct6116d) { + if (sio->type != nct6126d) { manid = superio_inw(addr, SIO_FINTEK_MANID); if (manid != SIO_FINTEK_ID) { pr_debug("Not a Fintek device at 0x%08x\n", addr); @@ -581,7 +581,7 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) err = 0; pr_info("Found %s at %#x\n", f7188x_names[sio->type], (unsigned int)addr); - if (sio->type != nct6116d) + if (sio->type != nct6126d) pr_info(" revision %d\n", superio_inb(addr, SIO_FINTEK_DEVREV)); err: diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c index 208fa851e82a..c14b5cc5e519 100644 --- a/drivers/gpio/gpio-fxl6408.c +++ b/drivers/gpio/gpio-fxl6408.c @@ -148,7 +148,7 @@ static struct i2c_driver fxl6408_driver = { .name = "fxl6408", .of_match_table = fxl6408_dt_ids, }, - .probe_new = fxl6408_probe, + .probe = fxl6408_probe, .id_table = fxl6408_id, }; module_i2c_driver(fxl6408_driver); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 5057fa9ad610..899335da93c7 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -125,7 +125,7 @@ static struct i2c_driver gw_pld_driver = { .name = "gw_pld", .of_match_table = gw_pld_dt_ids, }, - .probe_new = gw_pld_probe, + .probe = gw_pld_probe, .id_table = gw_pld_id, }; module_i2c_driver(gw_pld_driver); diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index 56656fb519f8..1e29de1671d4 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -199,7 +199,6 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct irq_domain *parent; - struct resource *res; struct ixp4xx_gpio *g; struct gpio_irq_chip *girq; struct device_node *irq_parent; @@ -210,8 +209,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) return -ENOMEM; g->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - g->base = devm_ioremap_resource(dev, res); + g->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(g->base)) return PTR_ERR(g->base); diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index d711ae06747e..ed3f653a1dfc 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c index cf482f4f0098..31c2b95321cc 100644 --- a/drivers/gpio/gpio-max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -62,7 +62,7 @@ static struct i2c_driver max7300_driver = { .driver = { .name = "max7300", }, - .probe_new = max7300_probe, + .probe = max7300_probe, .remove = max7300_remove, .id_table = max7300_id, }; diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 7f2fde191755..fca9ca68e387 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -711,7 +711,7 @@ static struct i2c_driver max732x_driver = { .name = "max732x", .of_match_table = of_match_ptr(max732x_of_table), }, - .probe_new = max732x_probe, + .probe = max732x_probe, .id_table = max732x_id, }; diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c new file mode 100644 index 000000000000..e30cee108986 --- /dev/null +++ b/drivers/gpio/gpio-mlxbf3.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause +/* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +/* + * There are 2 YU GPIO blocks: + * gpio[0]: HOST_GPIO0->HOST_GPIO31 + * gpio[1]: HOST_GPIO32->HOST_GPIO55 + */ +#define MLXBF3_GPIO_MAX_PINS_PER_BLOCK 32 + +/* + * fw_gpio[x] block registers and their offset + */ +#define MLXBF_GPIO_FW_OUTPUT_ENABLE_SET 0x00 +#define MLXBF_GPIO_FW_DATA_OUT_SET 0x04 + +#define MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR 0x00 +#define MLXBF_GPIO_FW_DATA_OUT_CLEAR 0x04 + +#define MLXBF_GPIO_CAUSE_RISE_EN 0x00 +#define MLXBF_GPIO_CAUSE_FALL_EN 0x04 +#define MLXBF_GPIO_READ_DATA_IN 0x08 + +#define MLXBF_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x00 +#define MLXBF_GPIO_CAUSE_OR_EVTEN0 0x14 +#define MLXBF_GPIO_CAUSE_OR_CLRCAUSE 0x18 + +struct mlxbf3_gpio_context { + struct gpio_chip gc; + + /* YU GPIO block address */ + void __iomem *gpio_set_io; + void __iomem *gpio_clr_io; + void __iomem *gpio_io; + + /* YU GPIO cause block address */ + void __iomem *gpio_cause_io; +}; + +static void mlxbf3_gpio_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc); + irq_hw_number_t offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + gpiochip_enable_irq(gc, offset); + + raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); + + val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); + val |= BIT(offset); + writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); +} + +static void mlxbf3_gpio_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc); + irq_hw_number_t offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); + val &= ~BIT(offset); + writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + + gpiochip_disable_irq(gc, offset); +} + +static irqreturn_t mlxbf3_gpio_irq_handler(int irq, void *ptr) +{ + struct mlxbf3_gpio_context *gs = ptr; + struct gpio_chip *gc = &gs->gc; + unsigned long pending; + u32 level; + + pending = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CAUSE_EVTEN0); + writel(pending, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); + + for_each_set_bit(level, &pending, gc->ngpio) + generic_handle_domain_irq(gc->irq.domain, level); + + return IRQ_RETVAL(pending); +} + +static int +mlxbf3_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf3_gpio_context *gs = gpiochip_get_data(gc); + irq_hw_number_t offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); + break; + case IRQ_TYPE_EDGE_RISING: + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_RISE_EN); + break; + case IRQ_TYPE_EDGE_FALLING: + val = readl(gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + MLXBF_GPIO_CAUSE_FALL_EN); + break; + default: + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + return -EINVAL; + } + + raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + + irq_set_handler_locked(irqd, handle_edge_irq); + + return 0; +} + +/* This function needs to be defined for handle_edge_irq() */ +static void mlxbf3_gpio_irq_ack(struct irq_data *data) +{ +} + +static const struct irq_chip gpio_mlxbf3_irqchip = { + .name = "MLNXBF33", + .irq_ack = mlxbf3_gpio_irq_ack, + .irq_set_type = mlxbf3_gpio_irq_set_type, + .irq_enable = mlxbf3_gpio_irq_enable, + .irq_disable = mlxbf3_gpio_irq_disable, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int mlxbf3_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mlxbf3_gpio_context *gs; + struct gpio_irq_chip *girq; + struct gpio_chip *gc; + int ret, irq; + + gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL); + if (!gs) + return -ENOMEM; + + gs->gpio_io = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gs->gpio_io)) + return PTR_ERR(gs->gpio_io); + + gs->gpio_cause_io = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(gs->gpio_cause_io)) + return PTR_ERR(gs->gpio_cause_io); + + gs->gpio_set_io = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(gs->gpio_set_io)) + return PTR_ERR(gs->gpio_set_io); + + gs->gpio_clr_io = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(gs->gpio_clr_io)) + return PTR_ERR(gs->gpio_clr_io); + gc = &gs->gc; + + ret = bgpio_init(gc, dev, 4, + gs->gpio_io + MLXBF_GPIO_READ_DATA_IN, + gs->gpio_set_io + MLXBF_GPIO_FW_DATA_OUT_SET, + gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR, + gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET, + gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0); + + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; + gc->owner = THIS_MODULE; + + irq = platform_get_irq(pdev, 0); + if (irq >= 0) { + girq = &gs->gc.irq; + gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip); + girq->default_type = IRQ_TYPE_NONE; + /* This will let us handle the parent IRQ in the driver */ + girq->num_parents = 0; + girq->parents = NULL; + girq->parent_handler = NULL; + girq->handler = handle_bad_irq; + + /* + * Directly request the irq here instead of passing + * a flow-handler because the irq is shared. + */ + ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler, + IRQF_SHARED, dev_name(dev), gs); + if (ret) + return dev_err_probe(dev, ret, "failed to request IRQ"); + } + + platform_set_drvdata(pdev, gs); + + ret = devm_gpiochip_add_data(dev, &gs->gc, gs); + if (ret) + dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); + + return 0; +} + +static const struct acpi_device_id mlxbf3_gpio_acpi_match[] = { + { "MLNXBF33", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, mlxbf3_gpio_acpi_match); + +static struct platform_driver mlxbf3_gpio_driver = { + .driver = { + .name = "mlxbf3_gpio", + .acpi_match_table = mlxbf3_gpio_acpi_match, + }, + .probe = mlxbf3_gpio_probe, +}; +module_platform_driver(mlxbf3_gpio_driver); + +MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver"); +MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index e6a7049bef64..b32063ac845a 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -369,7 +369,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, priv->offset = i; priv->desc = gpiochip_get_desc(gc, i); - debugfs_create_file(name, 0200, chip->dbg_dir, priv, + debugfs_create_file(name, 0600, chip->dbg_dir, priv, &gpio_mockup_debugfs_ops); } } diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 3eb08cd1fdc0..5979a36bf754 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -12,7 +12,6 @@ #include <linux/spinlock.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -375,8 +374,12 @@ static int mpc8xxx_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "fsl,qoriq-gpio") || of_device_is_compatible(np, "fsl,ls1028a-gpio") || of_device_is_compatible(np, "fsl,ls1088a-gpio") || - is_acpi_node(fwnode)) + is_acpi_node(fwnode)) { gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff); + /* Also, latch state of GPIOs configured as output by bootloader. */ + gc->bgpio_data = gc->read_reg(mpc8xxx_gc->regs + GPIO_DAT) & + gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR); + } ret = devm_gpiochip_add_data(&pdev->dev, gc, mpc8xxx_gc); if (ret) { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1286b22ef23a..a806a3c1b801 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1375,7 +1375,7 @@ static struct i2c_driver pca953x_driver = { .of_match_table = pca953x_dt_ids, .acpi_match_table = pca953x_acpi_ids, }, - .probe_new = pca953x_probe, + .probe = pca953x_probe, .remove = pca953x_remove, .id_table = pca953x_id, }; diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index 6a5a8e593ed5..d8db80ef1293 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -175,7 +175,7 @@ static struct i2c_driver pca9570_driver = { .name = "pca9570", .of_match_table = pca9570_of_match_table, }, - .probe_new = pca9570_probe, + .probe = pca9570_probe, .id_table = pca9570_id_table, }; module_i2c_driver(pca9570_driver); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 3de1d3ad7472..c4c785548408 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -419,7 +419,7 @@ static struct i2c_driver pcf857x_driver = { .name = "pcf857x", .of_match_table = pcf857x_of_table, }, - .probe_new = pcf857x_probe, + .probe = pcf857x_probe, .shutdown = pcf857x_shutdown, .id_table = pcf857x_id, }; diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index edff5e81489f..242dad763ac4 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -12,6 +12,7 @@ #include <soc/sa1100/pwer.h> #include <mach/hardware.h> #include <mach/irqs.h> +#include <mach/generic.h> struct sa1100_gpio_chip { struct gpio_chip chip; diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index da01e1cad7cb..ba4fccf3cc94 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -281,8 +281,6 @@ static int sch311x_gpio_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); - for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) { block = &priv->blocks[i]; @@ -305,42 +303,22 @@ static int sch311x_gpio_probe(struct platform_device *pdev) block->data_reg = sch311x_gpio_blocks[i].data_reg; block->runtime_reg = pdata->runtime_reg; - err = gpiochip_add_data(&block->chip, block); + err = devm_gpiochip_add_data(&pdev->dev, &block->chip, block); if (err < 0) { dev_err(&pdev->dev, "Could not register gpiochip, %d\n", err); - goto exit_err; + return err; } dev_info(&pdev->dev, "SMSC SCH311x GPIO block %d registered.\n", i); } return 0; - -exit_err: - /* release already registered chips */ - for (--i; i >= 0; i--) - gpiochip_remove(&priv->blocks[i].chip); - return err; -} - -static int sch311x_gpio_remove(struct platform_device *pdev) -{ - struct sch311x_gpio_priv *priv = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) { - gpiochip_remove(&priv->blocks[i].chip); - dev_info(&pdev->dev, - "SMSC SCH311x GPIO block %d unregistered.\n", i); - } - return 0; } static struct platform_driver sch311x_gpio_driver = { .driver.name = DRV_NAME, .probe = sch311x_gpio_probe, - .remove = sch311x_gpio_remove, }; diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index 98939cd4a71e..745e5f67254e 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -221,8 +221,12 @@ static int sifive_gpio_probe(struct platform_device *pdev) return -ENODEV; } - for (i = 0; i < ngpio; i++) - chip->irq_number[i] = platform_get_irq(pdev, i); + for (i = 0; i < ngpio; i++) { + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; + chip->irq_number[i] = ret; + } ret = bgpio_init(&chip->gc, dev, 4, chip->base + SIFIVE_GPIO_INPUT_VAL, diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index a1c8702f362c..8b49b0abacd5 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -696,6 +696,9 @@ static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank, char **line_names; list_for_each_entry(line, &bank->line_list, siblings) { + if (line->offset >= bank->num_lines) + continue; + if (line->name) { if (line->offset > max_offset) max_offset = line->offset; @@ -721,8 +724,13 @@ static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank, if (!line_names) return ERR_PTR(-ENOMEM); - list_for_each_entry(line, &bank->line_list, siblings) - line_names[line->offset] = line->name; + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->offset >= bank->num_lines) + continue; + + if (line->name && (line->offset <= max_offset)) + line_names[line->offset] = line->name; + } return line_names; } @@ -754,6 +762,9 @@ static int gpio_sim_add_hogs(struct gpio_sim_device *dev) list_for_each_entry(bank, &dev->bank_list, siblings) { list_for_each_entry(line, &bank->line_list, siblings) { + if (line->offset >= bank->num_lines) + continue; + if (line->hog) num_hogs++; } @@ -769,6 +780,9 @@ static int gpio_sim_add_hogs(struct gpio_sim_device *dev) list_for_each_entry(bank, &dev->bank_list, siblings) { list_for_each_entry(line, &bank->line_list, siblings) { + if (line->offset >= bank->num_lines) + continue; + if (!line->hog) continue; diff --git a/drivers/gpio/gpio-tangier.c b/drivers/gpio/gpio-tangier.c index e990781935ba..7ce3eddaed25 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/spinlock.h> @@ -428,10 +429,11 @@ static int tng_gpio_add_pin_ranges(struct gpio_chip *chip) int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio) { const struct tng_gpio_info *info = &gpio->info; + size_t nctx = DIV_ROUND_UP(info->ngpio, 32); struct gpio_irq_chip *girq; int ret; - gpio->ctx = devm_kcalloc(dev, DIV_ROUND_UP(info->ngpio, 32), sizeof(*gpio->ctx), GFP_KERNEL); + gpio->ctx = devm_kcalloc(dev, nctx, sizeof(*gpio->ctx), GFP_KERNEL); if (!gpio->ctx) return -ENOMEM; diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index b904de0b1784..80d08ddde40e 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -27,6 +27,22 @@ #define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4) +#define TEGRA186_GPIO_VM 0x00 +#define TEGRA186_GPIO_VM_RW_MASK 0x03 +#define TEGRA186_GPIO_SCR 0x04 +#define TEGRA186_GPIO_SCR_PIN_SIZE 0x08 +#define TEGRA186_GPIO_SCR_PORT_SIZE 0x40 +#define TEGRA186_GPIO_SCR_SEC_WEN BIT(28) +#define TEGRA186_GPIO_SCR_SEC_REN BIT(27) +#define TEGRA186_GPIO_SCR_SEC_G1W BIT(9) +#define TEGRA186_GPIO_SCR_SEC_G1R BIT(1) +#define TEGRA186_GPIO_FULL_ACCESS (TEGRA186_GPIO_SCR_SEC_WEN | \ + TEGRA186_GPIO_SCR_SEC_REN | \ + TEGRA186_GPIO_SCR_SEC_G1R | \ + TEGRA186_GPIO_SCR_SEC_G1W) +#define TEGRA186_GPIO_SCR_SEC_ENABLE (TEGRA186_GPIO_SCR_SEC_WEN | \ + TEGRA186_GPIO_SCR_SEC_REN) + /* control registers */ #define TEGRA186_GPIO_ENABLE_CONFIG 0x00 #define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0) @@ -81,6 +97,7 @@ struct tegra_gpio_soc { unsigned int num_pin_ranges; const char *pinmux; bool has_gte; + bool has_vm_support; }; struct tegra_gpio { @@ -130,6 +147,58 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, return gpio->base + offset + pin * 0x20; } +static void __iomem *tegra186_gpio_get_secure_base(struct tegra_gpio *gpio, + unsigned int pin) +{ + const struct tegra_gpio_port *port; + unsigned int offset; + + port = tegra186_gpio_get_port(gpio, &pin); + if (!port) + return NULL; + + offset = port->bank * 0x1000 + port->port * TEGRA186_GPIO_SCR_PORT_SIZE; + + return gpio->secure + offset + pin * TEGRA186_GPIO_SCR_PIN_SIZE; +} + +static inline bool tegra186_gpio_is_accessible(struct tegra_gpio *gpio, unsigned int pin) +{ + void __iomem *secure; + u32 value; + + secure = tegra186_gpio_get_secure_base(gpio, pin); + + if (gpio->soc->has_vm_support) { + value = readl(secure + TEGRA186_GPIO_VM); + if ((value & TEGRA186_GPIO_VM_RW_MASK) != TEGRA186_GPIO_VM_RW_MASK) + return false; + } + + value = __raw_readl(secure + TEGRA186_GPIO_SCR); + + if ((value & TEGRA186_GPIO_SCR_SEC_ENABLE) == 0) + return true; + + if ((value & TEGRA186_GPIO_FULL_ACCESS) == TEGRA186_GPIO_FULL_ACCESS) + return true; + + return false; +} + +static int tegra186_init_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, unsigned int ngpios) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + unsigned int j; + + for (j = 0; j < ngpios; j++) { + if (!tegra186_gpio_is_accessible(gpio, j)) + clear_bit(j, valid_mask); + } + return 0; +} + static int tegra186_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -816,6 +885,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.set = tegra186_gpio_set; gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; + gpio->gpio.init_valid_mask = tegra186_init_valid_mask; if (gpio->soc->has_gte) { gpio->gpio.en_hw_timestamp = tegra186_gpio_en_hw_ts; gpio->gpio.dis_hw_timestamp = tegra186_gpio_dis_hw_ts; @@ -894,11 +964,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (np) { - irq->parent_domain = irq_find_host(np); - of_node_put(np); - - if (!irq->parent_domain) - return -EPROBE_DEFER; + if (of_device_is_available(np)) { + irq->parent_domain = irq_find_host(np); + of_node_put(np); + + if (!irq->parent_domain) + return -EPROBE_DEFER; + } else { + of_node_put(np); + } } irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, @@ -958,6 +1032,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .name = "tegra186-gpio", .instance = 0, .num_irqs_per_bank = 1, + .has_vm_support = false, }; #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -985,6 +1060,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .name = "tegra186-gpio-aon", .instance = 1, .num_irqs_per_bank = 1, + .has_vm_support = false, }; #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -1040,6 +1116,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges), .pin_ranges = tegra194_main_pin_ranges, .pinmux = "nvidia,tegra194-pinmux", + .has_vm_support = true, }; #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -1065,6 +1142,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .instance = 1, .num_irqs_per_bank = 8, .has_gte = true, + .has_vm_support = false, }; #define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -1109,6 +1187,7 @@ static const struct tegra_gpio_soc tegra234_main_soc = { .name = "tegra234-gpio", .instance = 0, .num_irqs_per_bank = 8, + .has_vm_support = true, }; #define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -1135,6 +1214,7 @@ static const struct tegra_gpio_soc tegra234_aon_soc = { .instance = 1, .num_irqs_per_bank = 8, .has_gte = true, + .has_vm_support = false, }; #define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -1165,6 +1245,7 @@ static const struct tegra_gpio_soc tegra241_main_soc = { .name = "tegra241-gpio", .instance = 0, .num_irqs_per_bank = 8, + .has_vm_support = false, }; #define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -1186,6 +1267,7 @@ static const struct tegra_gpio_soc tegra241_aon_soc = { .name = "tegra241-gpio-aon", .instance = 1, .num_irqs_per_bank = 8, + .has_vm_support = false, }; static const struct of_device_id tegra186_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index 349c5fbd9b02..effb7b8ff81f 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ - * Andrew F. Davis <afd@ti.com> + * Copyright (C) 2015-2023 Texas Instruments Incorporated - https://www.ti.com/ + * Andrew Davis <afd@ti.com> */ #include <linux/gpio/driver.h> @@ -101,14 +101,11 @@ MODULE_DEVICE_TABLE(of, tpic2810_of_match_table); static int tpic2810_probe(struct i2c_client *client) { struct tpic2810 *gpio; - int ret; gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM; - i2c_set_clientdata(client, gpio); - gpio->chip = template_chip; gpio->chip.parent = &client->dev; @@ -116,20 +113,7 @@ static int tpic2810_probe(struct i2c_client *client) mutex_init(&gpio->lock); - ret = gpiochip_add_data(&gpio->chip, gpio); - if (ret < 0) { - dev_err(&client->dev, "Unable to register gpiochip\n"); - return ret; - } - - return 0; -} - -static void tpic2810_remove(struct i2c_client *client) -{ - struct tpic2810 *gpio = i2c_get_clientdata(client); - - gpiochip_remove(&gpio->chip); + return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); } static const struct i2c_device_id tpic2810_id_table[] = { @@ -143,12 +127,11 @@ static struct i2c_driver tpic2810_driver = { .name = "tpic2810", .of_match_table = tpic2810_of_match_table, }, - .probe_new = tpic2810_probe, - .remove = tpic2810_remove, + .probe = tpic2810_probe, .id_table = tpic2810_id_table, }; module_i2c_driver(tpic2810_driver); -MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_AUTHOR("Andrew Davis <afd@ti.com>"); MODULE_DESCRIPTION("TPIC2810 8-Bit LED Driver GPIO Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 1e9d8262d0ff..8f5827554e1e 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ - * Andrew F. Davis <afd@ti.com> + * Copyright (C) 2015-2023 Texas Instruments Incorporated - https://www.ti.com/ + * Andrew Davis <afd@ti.com> * * Based on the TPS65912 driver */ @@ -80,34 +80,16 @@ static const struct gpio_chip template_chip = { static int tps65086_gpio_probe(struct platform_device *pdev) { struct tps65086_gpio *gpio; - int ret; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM; - platform_set_drvdata(pdev, gpio); - gpio->tps = dev_get_drvdata(pdev->dev.parent); gpio->chip = template_chip; gpio->chip.parent = gpio->tps->dev; - ret = gpiochip_add_data(&gpio->chip, gpio); - if (ret < 0) { - dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); - return ret; - } - - return 0; -} - -static int tps65086_gpio_remove(struct platform_device *pdev) -{ - struct tps65086_gpio *gpio = platform_get_drvdata(pdev); - - gpiochip_remove(&gpio->chip); - - return 0; + return devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); } static const struct platform_device_id tps65086_gpio_id_table[] = { @@ -121,11 +103,10 @@ static struct platform_driver tps65086_gpio_driver = { .name = "tps65086-gpio", }, .probe = tps65086_gpio_probe, - .remove = tps65086_gpio_remove, .id_table = tps65086_gpio_id_table, }; module_platform_driver(tps65086_gpio_driver); -MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_AUTHOR("Andrew Davis <afd@ti.com>"); MODULE_DESCRIPTION("TPS65086 GPIO driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c new file mode 100644 index 000000000000..7b38aa360112 --- /dev/null +++ b/drivers/gpio/gpio-tps65219.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPIO driver for TI TPS65219 PMICs + * + * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include <linux/bits.h> +#include <linux/gpio/driver.h> +#include <linux/mfd/tps65219.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define TPS65219_GPIO0_DIR_MASK BIT(3) +#define TPS65219_GPIO0_OFFSET 2 +#define TPS65219_GPIO0_IDX 0 +#define TPS65219_GPIO_DIR_IN 1 +#define TPS65219_GPIO_DIR_OUT 0 + +struct tps65219_gpio { + struct gpio_chip gpio_chip; + struct tps65219 *tps; +}; + +static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + int ret, val; + + if (offset != TPS65219_GPIO0_IDX) + return GPIO_LINE_DIRECTION_OUT; + + ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); + if (ret) + return ret; + + return !!(val & TPS65219_GPIO0_DIR_MASK); +} + +static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + struct device *dev = gpio->tps->dev; + int ret, val; + + if (offset != TPS65219_GPIO0_IDX) { + dev_err(dev, "GPIO%d is output only, cannot get\n", offset); + return -ENOTSUPP; + } + + ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_CTRL, &val); + if (ret) + return ret; + + ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK)); + dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret); + + /* + * Depending on NVM config, return an error if direction is output, otherwise the GPIO0 + * status bit. + */ + + if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + return -ENOTSUPP; + + return ret; +} + +static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + struct device *dev = gpio->tps->dev; + int v, mask, bit; + + bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1; + + mask = BIT(bit); + v = value ? mask : 0; + + if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v)) + dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value); +} + +static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, + unsigned int direction) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + struct device *dev = gpio->tps->dev; + + /* + * Documentation is stating that GPIO0 direction must not be changed in Linux: + * Table 8-34. MFP_1_CONFIG(3): MULTI_DEVICE_ENABLE, should only be changed in INITIALIZE + * state (prior to ON Request). + * Set statically by NVM, changing direction in application can cause a hang. + * Below can be used for test purpose only. + */ + + if (IS_ENABLED(CONFIG_DEBUG_GPIO)) { + int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, + TPS65219_GPIO0_DIR_MASK, direction); + if (ret) { + dev_err(dev, + "GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n", + direction, offset); + return ret; + } + } + + dev_err(dev, + "GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n", + offset, direction); + + return -ENOTSUPP; +} + +static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + struct device *dev = gpio->tps->dev; + + if (offset != TPS65219_GPIO0_IDX) { + dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset); + return -ENOTSUPP; + } + + if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_IN) + return 0; + + return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_IN); +} + +static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) +{ + tps65219_gpio_set(gc, offset, value); + if (offset != TPS65219_GPIO0_IDX) + return 0; + + if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + return 0; + + return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_OUT); +} + +static const struct gpio_chip tps65219_template_chip = { + .label = "tps65219-gpio", + .owner = THIS_MODULE, + .get_direction = tps65219_gpio_get_direction, + .direction_input = tps65219_gpio_direction_input, + .direction_output = tps65219_gpio_direction_output, + .get = tps65219_gpio_get, + .set = tps65219_gpio_set, + .base = -1, + .ngpio = 3, + .can_sleep = true, +}; + +static int tps65219_gpio_probe(struct platform_device *pdev) +{ + struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); + struct tps65219_gpio *gpio; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->tps = tps; + gpio->gpio_chip = tps65219_template_chip; + gpio->gpio_chip.parent = tps->dev; + + return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio); +} + +static struct platform_driver tps65219_gpio_driver = { + .driver = { + .name = "tps65219-gpio", + }, + .probe = tps65219_gpio_probe, +}; +module_platform_driver(tps65219_gpio_driver); + +MODULE_ALIAS("platform:tps65219-gpio"); +MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>"); +MODULE_DESCRIPTION("TPS65219 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 43e8b66e04f7..eba96319dac2 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -185,7 +185,7 @@ static struct i2c_driver ts4900_gpio_driver = { .name = "ts4900-gpio", .of_match_table = ts4900_gpio_of_match_table, }, - .probe_new = ts4900_gpio_probe, + .probe = ts4900_gpio_probe, .id_table = ts4900_gpio_id_table, }; module_i2c_driver(ts4900_gpio_driver); diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index c1bb2c3ca6f2..bcd692229c7c 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -17,7 +17,9 @@ #include <linux/interrupt.h> #include <linux/kthread.h> #include <linux/irq.h> +#include <linux/gpio/machine.h> #include <linux/gpio/driver.h> +#include <linux/gpio/consumer.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/irqdomain.h> @@ -465,8 +467,7 @@ static int gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) REG_GPIO_DEBEN1, 3); } -static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev, - struct twl4030_gpio_platform_data *pdata) +static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev) { struct twl4030_gpio_platform_data *omap_twl_info; @@ -474,9 +475,6 @@ static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev, if (!omap_twl_info) return NULL; - if (pdata) - *omap_twl_info = *pdata; - omap_twl_info->use_leds = of_property_read_bool(dev->of_node, "ti,use-leds"); @@ -492,21 +490,18 @@ static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev, return omap_twl_info; } -/* Cannot use as gpio_twl4030_probe() calls us */ -static int gpio_twl4030_remove(struct platform_device *pdev) +/* Called from the registered devm action */ +static void gpio_twl4030_power_off_action(void *data) { - struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev); - - gpiochip_remove(&priv->gpio_chip); + struct gpio_desc *d = data; - /* REVISIT no support yet for deregistering all the IRQs */ - WARN_ON(!is_module()); - return 0; + gpiod_unexport(d); + gpiochip_free_own_desc(d); } static int gpio_twl4030_probe(struct platform_device *pdev) { - struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct twl4030_gpio_platform_data *pdata; struct device_node *node = pdev->dev.of_node; struct gpio_twl4030_priv *priv; int ret, irq_base; @@ -546,9 +541,7 @@ no_irqs: mutex_init(&priv->mutex); - if (node) - pdata = of_gpio_twl4030(&pdev->dev, pdata); - + pdata = of_gpio_twl4030(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "Platform data is missing\n"); return -ENXIO; @@ -577,27 +570,39 @@ no_irqs: if (pdata->use_leds) priv->gpio_chip.ngpio += 2; - ret = gpiochip_add_data(&priv->gpio_chip, priv); + ret = devm_gpiochip_add_data(&pdev->dev, &priv->gpio_chip, priv); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); priv->gpio_chip.ngpio = 0; - gpio_twl4030_remove(pdev); - goto out; + return ret; } - platform_set_drvdata(pdev, priv); + /* + * Special quirk for the OMAP3 to hog and export a WLAN power + * GPIO. + */ + if (IS_ENABLED(CONFIG_ARCH_OMAP3) && + of_machine_is_compatible("compulab,omap3-sbc-t3730")) { + struct gpio_desc *d; + + d = gpiochip_request_own_desc(&priv->gpio_chip, + 2, "wlan pwr", + GPIO_ACTIVE_HIGH, + GPIOD_OUT_HIGH); + if (IS_ERR(d)) + return dev_err_probe(&pdev->dev, PTR_ERR(d), + "unable to hog wlan pwr GPIO\n"); + + gpiod_export(d, 0); - if (pdata->setup) { - int status; + ret = devm_add_action_or_reset(&pdev->dev, gpio_twl4030_power_off_action, d); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to install power off handler\n"); - status = pdata->setup(&pdev->dev, priv->gpio_chip.base, - TWL4030_GPIO_MAX); - if (status) - dev_dbg(&pdev->dev, "setup --> %d\n", status); } -out: - return ret; + return 0; } static const struct of_device_id twl_gpio_match[] = { @@ -615,7 +620,6 @@ static struct platform_driver gpio_twl4030_driver = { .of_match_table = twl_gpio_match, }, .probe = gpio_twl4030_probe, - .remove = gpio_twl4030_remove, }; static int __init gpio_twl4030_init(void) diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 51d6119e1bb4..bbc06cdd9634 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/seq_file.h> #include <linux/spi/spi.h> #include <linux/regmap.h> diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 06c6401f02b8..0a7264aabe48 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -151,8 +151,8 @@ struct zynq_platform_data { int bank_max[ZYNQMP_GPIO_MAX_BANK]; }; -static struct irq_chip zynq_gpio_level_irqchip; -static struct irq_chip zynq_gpio_edge_irqchip; +static const struct irq_chip zynq_gpio_level_irqchip; +static const struct irq_chip zynq_gpio_edge_irqchip; /** * zynq_gpio_is_zynq - test if HW is zynq or zynqmp @@ -404,9 +404,12 @@ static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) static void zynq_gpio_irq_mask(struct irq_data *irq_data) { unsigned int device_pin_num, bank_num, bank_pin_num; + const unsigned long offset = irqd_to_hwirq(irq_data); + struct gpio_chip *chip = irq_data_get_irq_chip_data(irq_data); struct zynq_gpio *gpio = gpiochip_get_data(irq_data_get_irq_chip_data(irq_data)); + gpiochip_disable_irq(chip, offset); device_pin_num = irq_data->hwirq; zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio); writel_relaxed(BIT(bank_pin_num), @@ -425,9 +428,12 @@ static void zynq_gpio_irq_mask(struct irq_data *irq_data) static void zynq_gpio_irq_unmask(struct irq_data *irq_data) { unsigned int device_pin_num, bank_num, bank_pin_num; + const unsigned long offset = irqd_to_hwirq(irq_data); + struct gpio_chip *chip = irq_data_get_irq_chip_data(irq_data); struct zynq_gpio *gpio = gpiochip_get_data(irq_data_get_irq_chip_data(irq_data)); + gpiochip_enable_irq(chip, offset); device_pin_num = irq_data->hwirq; zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio); writel_relaxed(BIT(bank_pin_num), @@ -569,28 +575,8 @@ 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_resume_and_get(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 = { +static const struct irq_chip zynq_gpio_level_irqchip = { .name = DRIVER_NAME, .irq_enable = zynq_gpio_irq_enable, .irq_eoi = zynq_gpio_irq_ack, @@ -598,13 +584,12 @@ 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, + IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; -static struct irq_chip zynq_gpio_edge_irqchip = { +static const struct irq_chip zynq_gpio_edge_irqchip = { .name = DRIVER_NAME, .irq_enable = zynq_gpio_irq_enable, .irq_ack = zynq_gpio_irq_ack, @@ -612,9 +597,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, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, @@ -962,7 +946,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) /* Set up the GPIO irqchip */ girq = &chip->irq; - girq->chip = &zynq_gpio_edge_irqchip; + gpio_irq_chip_set_chip(girq, &zynq_gpio_edge_irqchip); girq->parent_handler = zynq_gpio_irqhandler; girq->num_parents = 1; girq->parents = devm_kcalloc(&pdev->dev, 1, diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 30e2476a6dc4..97f4b498e343 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -33,12 +33,6 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) if (err) return err; - if (flags & GPIOF_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); - - if (flags & GPIOF_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (flags & GPIOF_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); @@ -51,12 +45,6 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) if (err) goto free_gpio; - if (flags & GPIOF_EXPORT) { - err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE); - if (err) - goto free_gpio; - } - return 0; free_gpio: diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 04fb05df805b..251c875b5c34 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -209,6 +209,8 @@ static int gpiochip_find_base(int ngpio) break; /* nope, check the space right after the chip */ base = gdev->base + gdev->ngpio; + if (base < GPIO_DYNAMIC_BASE) + base = GPIO_DYNAMIC_BASE; } if (gpio_is_valid(base)) { @@ -463,6 +465,12 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) return p; } +static void gpiochip_free_mask(unsigned long **p) +{ + bitmap_free(*p); + *p = NULL; +} + static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc) { struct device *dev = &gc->gpiodev->dev; @@ -476,18 +484,6 @@ static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc) return 0; } -static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) -{ - if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask)) - return 0; - - gc->valid_mask = gpiochip_allocate_mask(gc); - if (!gc->valid_mask) - return -ENOMEM; - - return 0; -} - static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc) { struct device *dev = &gc->gpiodev->dev; @@ -528,6 +524,13 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) { int ret; + if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask)) + return 0; + + gc->valid_mask = gpiochip_allocate_mask(gc); + if (!gc->valid_mask) + return -ENOMEM; + ret = gpiochip_apply_reserved_ranges(gc); if (ret) return ret; @@ -542,8 +545,7 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { - bitmap_free(gc->valid_mask); - gc->valid_mask = NULL; + gpiochip_free_mask(&gc->valid_mask); } static int gpiochip_add_pin_ranges(struct gpio_chip *gc) @@ -855,7 +857,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_remove_from_list; - ret = gpiochip_alloc_valid_mask(gc); + ret = gpiochip_init_valid_mask(gc); if (ret) goto err_remove_from_list; @@ -863,10 +865,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_free_gpiochip_mask; - ret = gpiochip_init_valid_mask(gc); - if (ret) - goto err_remove_of_chip; - for (i = 0; i < gc->ngpio; i++) { struct gpio_desc *desc = &gdev->descs[i]; @@ -1087,8 +1085,7 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { - bitmap_free(gc->irq.valid_mask); - gc->irq.valid_mask = NULL; + gpiochip_free_mask(&gc->irq.valid_mask); } bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, @@ -1674,11 +1671,10 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, if (ret) return ret; } else { - /* Some drivers provide custom irqdomain ops */ gc->irq.domain = irq_domain_create_simple(fwnode, gc->ngpio, gc->irq.first, - gc->irq.domain_ops ?: &gpiochip_domain_ops, + &gpiochip_domain_ops, gc); if (!gc->irq.domain) return -EINVAL; @@ -1743,7 +1739,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) } /* Remove all IRQ mappings and delete the domain */ - if (gc->irq.domain) { + if (!gc->irq.domain_is_allocated_externally && gc->irq.domain) { unsigned int irq; for (offset = 0; offset < gc->ngpio; offset++) { @@ -1789,6 +1785,15 @@ int gpiochip_irqchip_add_domain(struct gpio_chip *gc, gc->to_irq = gpiochip_to_irq; gc->irq.domain = domain; + gc->irq.domain_is_allocated_externally = true; + + /* + * Using barrier() here to prevent compiler from reordering + * gc->irq.initialized before adding irqdomain. + */ + barrier(); + + gc->irq.initialized = true; return 0; } @@ -2122,8 +2127,6 @@ static bool gpiod_free_commit(struct gpio_desc *desc) might_sleep(); - gpiod_unexport(desc); - spin_lock_irqsave(&gpio_lock, flags); gc = desc->gdev->chip; @@ -4241,7 +4244,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, /* Mark GPIO as hogged so it can be identified and removed later */ set_bit(FLAG_IS_HOGGED, &desc->flags); - gpiod_info(desc, "hogged as %s%s\n", + gpiod_dbg(desc, "hogged as %s%s\n", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? (dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : ""); |