diff options
| author | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2024-04-02 08:17:13 -0400 |
|---|---|---|
| committer | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2024-04-02 08:17:13 -0400 |
| commit | 5add703f6acad1c63f8a532b6de56e50d548e904 (patch) | |
| tree | be39a82716119cd8b7cf7e2734baf8cd9a80ceb7 /drivers/gpio | |
| parent | 71cdfa8cad300caa7afcbba09776bbc8dcfa9e39 (diff) | |
| parent | 39cd87c4eb2b893354f3b850f916353f2658ae6f (diff) | |
| download | linux-5add703f6acad1c63f8a532b6de56e50d548e904.tar.gz linux-5add703f6acad1c63f8a532b6de56e50d548e904.tar.bz2 linux-5add703f6acad1c63f8a532b6de56e50d548e904.zip | |
Merge drm/drm-next into drm-intel-next
Catching up on 6.9-rc2
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Diffstat (limited to 'drivers/gpio')
| -rw-r--r-- | drivers/gpio/Kconfig | 27 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpio-74x164.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/gpio-cros-ec.c | 209 | ||||
| -rw-r--r-- | drivers/gpio/gpio-eic-sprd.c | 10 | ||||
| -rw-r--r-- | drivers/gpio/gpio-mvebu.c | 18 | ||||
| -rw-r--r-- | drivers/gpio/gpio-nomadik.c | 730 | ||||
| -rw-r--r-- | drivers/gpio/gpio-sim.c | 49 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-acpi.c | 15 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-acpi.h | 5 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-cdev.c | 134 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-devres.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-legacy.c | 12 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-of.c | 29 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-of.h | 6 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 150 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.c | 1007 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.h | 95 |
18 files changed, 1822 insertions, 682 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1301cec94f12..b50d0b470849 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -478,6 +478,18 @@ config GPIO_MXS select GPIO_GENERIC select GENERIC_IRQ_CHIP +config GPIO_NOMADIK + bool "Nomadik GPIO driver" + depends on ARCH_U8500 || ARCH_NOMADIK || MACH_EYEQ5 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + Say yes here to support the Nomadik SoC GPIO block. This block is also + used by the Mobileye EyeQ5 SoC. + + It handles up to 32 GPIOs per bank, that can all be interrupt sources. + It is deeply interconnected with the associated pinctrl driver as GPIO + registers handle muxing ("alternate functions") as well. + config GPIO_NPCM_SGPIO bool "Nuvoton SGPIO support" depends on ARCH_NPCM || COMPILE_TEST @@ -711,8 +723,9 @@ config GPIO_UNIPHIER Say yes here to support UniPhier GPIOs. config GPIO_VF610 - def_bool y - depends on ARCH_MXC + bool "VF610 GPIO support" + default y if SOC_VF610 + depends on ARCH_MXC || COMPILE_TEST select GPIOLIB_IRQCHIP help Say yes here to support i.MX or Vybrid vf610 GPIOs. @@ -1240,6 +1253,16 @@ config GPIO_BD9571MWV This driver can also be built as a module. If so, the module will be called gpio-bd9571mwv. +config GPIO_CROS_EC + tristate "ChromeOS EC GPIO support" + depends on CROS_EC + help + GPIO driver for the ChromeOS Embedded Controller (EC). GPIOs + cannot be set unless the system is unlocked. + + This driver can also be built as a module. If so, the module + will be called gpio-cros-ec. + config GPIO_CRYSTAL_COVE tristate "GPIO support for Crystal Cove PMIC" depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9e40af196aae..fdd28c58d890 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o +obj-$(CONFIG_GPIO_CROS_EC) += gpio-cros-ec.o obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o @@ -116,6 +117,7 @@ obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o +obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index e00c33310517..753e7be039e4 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -127,8 +127,6 @@ static int gen_74x164_probe(struct spi_device *spi) if (IS_ERR(chip->gpiod_oe)) return PTR_ERR(chip->gpiod_oe); - gpiod_set_value_cansleep(chip->gpiod_oe, 1); - spi_set_drvdata(spi, chip); chip->gpio_chip.label = spi->modalias; @@ -153,6 +151,8 @@ static int gen_74x164_probe(struct spi_device *spi) goto exit_destroy; } + gpiod_set_value_cansleep(chip->gpiod_oe, 1); + ret = gpiochip_add_data(&chip->gpio_chip, chip); if (!ret) return 0; diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c new file mode 100644 index 000000000000..842e1c060414 --- /dev/null +++ b/drivers/gpio/gpio-cros-ec.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2024 Google LLC + * + * This driver provides the ability to control GPIOs on the Chrome OS EC. + * There isn't any direction control, and setting values on GPIOs is only + * possible when the system is unlocked. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> + +/* Prefix all names to avoid collisions with EC <-> AP nets */ +static const char cros_ec_gpio_prefix[] = "EC:"; + +/* Setting gpios is only supported when the system is unlocked */ +static void cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_set params = { + .val = val, + }; + int ret; + ssize_t copied; + + copied = strscpy(params.name, name, sizeof(params.name)); + if (copied < 0) + return; + + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, ¶ms, + sizeof(params), NULL, 0); + if (ret < 0) + dev_err(gc->parent, "error setting gpio%d (%s) on EC: %d\n", gpio, name, ret); +} + +static int cros_ec_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_get params; + struct ec_response_gpio_get response; + int ret; + ssize_t copied; + + copied = strscpy(params.name, name, sizeof(params.name)); + if (copied < 0) + return -EINVAL; + + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err(gc->parent, "error getting gpio%d (%s) on EC: %d\n", gpio, name, ret); + return ret; + } + + return response.val; +} + +#define CROS_EC_GPIO_INPUT BIT(8) +#define CROS_EC_GPIO_OUTPUT BIT(9) + +static int cros_ec_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + .get_info.index = gpio, + }; + struct ec_response_gpio_get_v1 response; + int ret; + + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err(gc->parent, "error getting direction of gpio%d (%s) on EC: %d\n", gpio, name, ret); + return ret; + } + + if (response.get_info.flags & CROS_EC_GPIO_INPUT) + return GPIO_LINE_DIRECTION_IN; + + if (response.get_info.flags & CROS_EC_GPIO_OUTPUT) + return GPIO_LINE_DIRECTION_OUT; + + return -EINVAL; +} + +/* Query EC for all gpio line names */ +static int cros_ec_gpio_init_names(struct cros_ec_device *cros_ec, struct gpio_chip *gc) +{ + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + }; + struct ec_response_gpio_get_v1 response; + int ret, i; + /* EC may not NUL terminate */ + size_t name_len = strlen(cros_ec_gpio_prefix) + sizeof(response.get_info.name) + 1; + ssize_t copied; + const char **names; + char *str; + + names = devm_kcalloc(gc->parent, gc->ngpio, sizeof(*names), GFP_KERNEL); + if (!names) + return -ENOMEM; + gc->names = names; + + str = devm_kcalloc(gc->parent, gc->ngpio, name_len, GFP_KERNEL); + if (!str) + return -ENOMEM; + + /* Get gpio line names one at a time */ + for (i = 0; i < gc->ngpio; i++) { + params.get_info.index = i; + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err_probe(gc->parent, ret, "error getting gpio%d info\n", i); + return ret; + } + + names[i] = str; + copied = scnprintf(str, name_len, "%s%s", cros_ec_gpio_prefix, + response.get_info.name); + if (copied < 0) + return copied; + + str += copied + 1; + } + + return 0; +} + +/* Query EC for number of gpios */ +static int cros_ec_gpio_ngpios(struct cros_ec_device *cros_ec) +{ + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_COUNT, + }; + struct ec_response_gpio_get_v1 response; + int ret; + + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) + return ret; + + return response.get_count.val; +} + +static int cros_ec_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + struct cros_ec_dev *ec_dev = dev_get_drvdata(parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct gpio_chip *gc; + int ngpios; + int ret; + + /* Use the fwnode from the protocol device, e.g. cros-ec-spi */ + device_set_node(dev, dev_fwnode(cros_ec->dev)); + + ngpios = cros_ec_gpio_ngpios(cros_ec); + if (ngpios < 0) { + dev_err_probe(dev, ngpios, "error getting gpio count\n"); + return ngpios; + } + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->ngpio = ngpios; + gc->parent = dev; + ret = cros_ec_gpio_init_names(cros_ec, gc); + if (ret) + return ret; + + gc->can_sleep = true; + gc->label = dev_name(dev); + gc->base = -1; + gc->set = cros_ec_gpio_set; + gc->get = cros_ec_gpio_get; + gc->get_direction = cros_ec_gpio_get_direction; + + return devm_gpiochip_add_data(dev, gc, cros_ec); +} + +static struct platform_driver cros_ec_gpio_driver = { + .probe = cros_ec_gpio_probe, + .driver = { + .name = "cros-ec-gpio", + }, +}; +module_platform_driver(cros_ec_gpio_driver); + +MODULE_DESCRIPTION("ChromeOS EC GPIO Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 806b88d8dfb7..2dd0e46c42ad 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -108,7 +108,6 @@ static struct sprd_eic *to_sprd_eic(struct notifier_block *nb) struct sprd_eic_variant_data { enum sprd_eic_type type; - u32 num_eics; }; static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { @@ -118,22 +117,18 @@ static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = { .type = SPRD_EIC_DEBOUNCE, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_latch_data = { .type = SPRD_EIC_LATCH, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_async_data = { .type = SPRD_EIC_ASYNC, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_sync_data = { .type = SPRD_EIC_SYNC, - .num_eics = 8, }; static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic, @@ -619,6 +614,7 @@ static int sprd_eic_probe(struct platform_device *pdev) struct gpio_irq_chip *irq; struct sprd_eic *sprd_eic; struct resource *res; + u16 num_banks = 0; int ret, i; pdata = of_device_get_match_data(dev); @@ -652,10 +648,12 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->base[i] = devm_ioremap_resource(dev, res); if (IS_ERR(sprd_eic->base[i])) return PTR_ERR(sprd_eic->base[i]); + + num_banks++; } sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type]; - sprd_eic->chip.ngpio = pdata->num_eics; + sprd_eic->chip.ngpio = num_banks * SPRD_EIC_PER_BANK_NR; sprd_eic->chip.base = -1; sprd_eic->chip.parent = dev; sprd_eic->chip.direction_input = sprd_eic_direction_input; diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index a13f3c18ccd4..8cfd3a89c018 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -99,7 +99,6 @@ struct mvebu_pwm { u32 offset; unsigned long clk_rate; struct gpio_desc *gpiod; - struct pwm_chip chip; spinlock_t lock; struct mvebu_gpio_chip *mvchip; @@ -615,7 +614,7 @@ static const struct regmap_config mvebu_gpio_regmap_config = { */ static struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip) { - return container_of(chip, struct mvebu_pwm, chip); + return pwmchip_get_drvdata(chip); } static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -789,6 +788,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct mvebu_pwm *mvpwm; + struct pwm_chip *chip; void __iomem *base; u32 offset; u32 set; @@ -813,9 +813,11 @@ static int mvebu_pwm_probe(struct platform_device *pdev, if (IS_ERR(mvchip->clk)) return PTR_ERR(mvchip->clk); - mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); - if (!mvpwm) - return -ENOMEM; + chip = devm_pwmchip_alloc(dev, mvchip->chip.ngpio, sizeof(*mvpwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + mvpwm = to_mvebu_pwm(chip); + mvchip->mvpwm = mvpwm; mvpwm->mvchip = mvchip; mvpwm->offset = offset; @@ -868,13 +870,11 @@ static int mvebu_pwm_probe(struct platform_device *pdev, return -EINVAL; } - mvpwm->chip.dev = dev; - mvpwm->chip.ops = &mvebu_pwm_ops; - mvpwm->chip.npwm = mvchip->chip.ngpio; + chip->ops = &mvebu_pwm_ops; spin_lock_init(&mvpwm->lock); - return devm_pwmchip_add(dev, &mvpwm->chip); + return devm_pwmchip_add(dev, chip); } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c new file mode 100644 index 000000000000..836f1cc760c2 --- /dev/null +++ b/drivers/gpio/gpio-nomadik.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driver for the IP block found in the Nomadik SoC; it is an AMBA device, + * managing 32 pins with alternate functions. It can also handle the STA2X11 + * block from ST. + * + * The GPIO chips are shared with pinctrl-nomadik if used; it needs access for + * pinmuxing functionality and others. + * + * This driver also handles the mobileye,eyeq5-gpio compatible. It is an STA2X11 + * but with only data, direction and interrupts register active. We want to + * avoid touching SLPM, RWIMSC, FWIMSC, AFSLA and AFSLB registers; that is, + * wake and alternate function registers. It is NOT compatible with + * pinctrl-nomadik. + * + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> + * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> + * Copyright (C) 2011-2013 Linus Walleij <linus.walleij@linaro.org> + */ +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <linux/gpio/gpio-nomadik.h> + +#ifndef CONFIG_PINCTRL_NOMADIK +static DEFINE_SPINLOCK(nmk_gpio_slpm_lock); +#endif + +void __nmk_gpio_set_slpm(struct nmk_gpio_chip *nmk_chip, unsigned int offset, + enum nmk_gpio_slpm mode) +{ + u32 slpm; + + /* We should NOT have been called. */ + if (WARN_ON(nmk_chip->is_mobileye_soc)) + return; + + slpm = readl(nmk_chip->addr + NMK_GPIO_SLPC); + if (mode == NMK_GPIO_SLPM_NOCHANGE) + slpm |= BIT(offset); + else + slpm &= ~BIT(offset); + writel(slpm, nmk_chip->addr + NMK_GPIO_SLPC); +} + +static void __nmk_gpio_set_output(struct nmk_gpio_chip *nmk_chip, + unsigned int offset, int val) +{ + if (val) + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DATS); + else + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DATC); +} + +void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, + unsigned int offset, int val) +{ + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DIRS); + __nmk_gpio_set_output(nmk_chip, offset, val); +} + +/* IRQ functions */ + +static void nmk_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + clk_enable(nmk_chip->clk); + writel(BIT(d->hwirq), nmk_chip->addr + NMK_GPIO_IC); + clk_disable(nmk_chip->clk); +} + +enum nmk_gpio_irq_type { + NORMAL, + WAKE, +}; + +static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, + int offset, enum nmk_gpio_irq_type which, + bool enable) +{ + u32 *rimscval; + u32 *fimscval; + u32 rimscreg; + u32 fimscreg; + + if (which == NORMAL) { + rimscreg = NMK_GPIO_RIMSC; + fimscreg = NMK_GPIO_FIMSC; + rimscval = &nmk_chip->rimsc; + fimscval = &nmk_chip->fimsc; + } else { + /* We should NOT have been called. */ + if (WARN_ON(nmk_chip->is_mobileye_soc)) + return; + rimscreg = NMK_GPIO_RWIMSC; + fimscreg = NMK_GPIO_FWIMSC; + rimscval = &nmk_chip->rwimsc; + fimscval = &nmk_chip->fwimsc; + } + + /* we must individually set/clear the two edges */ + if (nmk_chip->edge_rising & BIT(offset)) { + if (enable) + *rimscval |= BIT(offset); + else + *rimscval &= ~BIT(offset); + writel(*rimscval, nmk_chip->addr + rimscreg); + } + if (nmk_chip->edge_falling & BIT(offset)) { + if (enable) + *fimscval |= BIT(offset); + else + *fimscval &= ~BIT(offset); + writel(*fimscval, nmk_chip->addr + fimscreg); + } +} + +static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip, + int offset, bool on) +{ + /* We should NOT have been called. */ + if (WARN_ON(nmk_chip->is_mobileye_soc)) + return; + + /* + * Ensure WAKEUP_ENABLE is on. No need to disable it if wakeup is + * disabled, since setting SLPM to 1 increases power consumption, and + * wakeup is anyhow controlled by the RIMSC and FIMSC registers. + */ + if (nmk_chip->sleepmode && on) { + __nmk_gpio_set_slpm(nmk_chip, offset, + NMK_GPIO_SLPM_WAKEUP_ENABLE); + } + + __nmk_gpio_irq_modify(nmk_chip, offset, WAKE, on); +} + +static void nmk_gpio_irq_maskunmask(struct nmk_gpio_chip *nmk_chip, + struct irq_data *d, bool enable) +{ + unsigned long flags; + + clk_enable(nmk_chip->clk); + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, NORMAL, enable); + + if (!nmk_chip->is_mobileye_soc && !(nmk_chip->real_wake & BIT(d->hwirq))) + __nmk_gpio_set_wake(nmk_chip, d->hwirq, enable); + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); +} + +static void nmk_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + nmk_gpio_irq_maskunmask(nmk_chip, d, false); + gpiochip_disable_irq(gc, irqd_to_hwirq(d)); +} + +static void nmk_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + gpiochip_enable_irq(gc, irqd_to_hwirq(d)); + nmk_gpio_irq_maskunmask(nmk_chip, d, true); +} + +static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + unsigned long flags; + + /* Handler is registered in all cases. */ + if (nmk_chip->is_mobileye_soc) + return -ENXIO; + + clk_enable(nmk_chip->clk); + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + if (irqd_irq_disabled(d)) + __nmk_gpio_set_wake(nmk_chip, d->hwirq, on); + + if (on) + nmk_chip->real_wake |= BIT(d->hwirq); + else + nmk_chip->real_wake &= ~BIT(d->hwirq); + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); + + return 0; +} + +static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + bool enabled = !irqd_irq_disabled(d); + bool wake = irqd_is_wakeup_set(d); + unsigned long flags; + + if (type & IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + if (type & IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + + clk_enable(nmk_chip->clk); + spin_lock_irqsave(&nmk_chip->lock, flags); + + if (enabled) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, NORMAL, false); + + if (!nmk_chip->is_mobileye_soc && (enabled || wake)) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, WAKE, false); + + nmk_chip->edge_rising &= ~BIT(d->hwirq); + if (type & IRQ_TYPE_EDGE_RISING) + nmk_chip->edge_rising |= BIT(d->hwirq); + + nmk_chip->edge_falling &= ~BIT(d->hwirq); + if (type & IRQ_TYPE_EDGE_FALLING) + nmk_chip->edge_falling |= BIT(d->hwirq); + + if (enabled) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, NORMAL, true); + + if (!nmk_chip->is_mobileye_soc && (enabled || wake)) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, WAKE, true); + + spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); + + return 0; +} + +static unsigned int nmk_gpio_irq_startup(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + clk_enable(nmk_chip->clk); + nmk_gpio_irq_unmask(d); + return 0; +} + +static void nmk_gpio_irq_shutdown(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + nmk_gpio_irq_mask(d); + clk_disable(nmk_chip->clk); +} + +static irqreturn_t nmk_gpio_irq_handler(int irq, void *dev_id) +{ + struct nmk_gpio_chip *nmk_chip = dev_id; + struct gpio_chip *chip = &nmk_chip->chip; + unsigned long mask = GENMASK(chip->ngpio - 1, 0); + unsigned long status; + int bit; + + clk_enable(nmk_chip->clk); + + status = readl(nmk_chip->addr + NMK_GPIO_IS); + + /* Ensure we cannot leave pending bits; this should never occur. */ + if (unlikely(status & ~mask)) + writel(status & ~mask, nmk_chip->addr + NMK_GPIO_IC); + + clk_disable(nmk_chip->clk); + + for_each_set_bit(bit, &status, chip->ngpio) + generic_handle_domain_irq_safe(chip->irq.domain, bit); + + return IRQ_RETVAL((status & mask) != 0); +} + +/* I/O Functions */ + +static int nmk_gpio_get_dir(struct gpio_chip *chip, unsigned int offset) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + int dir; + + clk_enable(nmk_chip->clk); + + dir = readl(nmk_chip->addr + NMK_GPIO_DIR) & BIT(offset); + + clk_disable(nmk_chip->clk); + + if (dir) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned int offset) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + + clk_enable(nmk_chip->clk); + + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DIRC); + + clk_disable(nmk_chip->clk); + + return 0; +} + +static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned int offset) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + int value; + + clk_enable(nmk_chip->clk); + + value = !!(readl(nmk_chip->addr + NMK_GPIO_DAT) & BIT(offset)); + + clk_disable(nmk_chip->clk); + + return value; +} + +static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, + int val) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + + clk_enable(nmk_chip->clk); + + __nmk_gpio_set_output(nmk_chip, offset, val); + + clk_disable(nmk_chip->clk); +} + +static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned int offset, + int val) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + + clk_enable(nmk_chip->clk); + + __nmk_gpio_make_output(nmk_chip, offset, val); + + clk_disable(nmk_chip->clk); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS + +static int nmk_gpio_get_mode(struct nmk_gpio_chip *nmk_chip, int offset) +{ + u32 afunc, bfunc; + + /* We don't support modes. */ + if (nmk_chip->is_mobileye_soc) + return NMK_GPIO_ALT_GPIO; + + clk_enable(nmk_chip->clk); + + afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & BIT(offset); + bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & BIT(offset); + + clk_disable(nmk_chip->clk); + + return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); +} + +void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev, + struct gpio_chip *chip, unsigned int offset, + unsigned int gpio) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + int mode; + bool is_out; + bool data_out; + bool pull; + static const char * const modes[] = { + [NMK_GPIO_ALT_GPIO] = "gpio", + [NMK_GPIO_ALT_A] = "altA", + [NMK_GPIO_ALT_B] = "altB", + [NMK_GPIO_ALT_C] = "altC", + [NMK_GPIO_ALT_C + 1] = "altC1", + [NMK_GPIO_ALT_C + 2] = "altC2", + [NMK_GPIO_ALT_C + 3] = "altC3", + [NMK_GPIO_ALT_C + 4] = "altC4", + }; + + char *label = gpiochip_dup_line_label(chip, offset); + if (IS_ERR(label)) + return; + + clk_enable(nmk_chip->clk); + is_out = !!(readl(nmk_chip->addr + NMK_GPIO_DIR) & BIT(offset)); + pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & BIT(offset)); + data_out = !!(readl(nmk_chip->addr + NMK_GPIO_DAT) & BIT(offset)); + mode = nmk_gpio_get_mode(nmk_chip, offset); +#ifdef CONFIG_PINCTRL_NOMADIK + if (mode == NMK_GPIO_ALT_C && pctldev) + mode = nmk_prcm_gpiocr_get_mode(pctldev, gpio); +#endif + + if (is_out) { + seq_printf(s, " gpio-%-3d (%-20.20s) out %s %s", + gpio, + label ?: "(none)", + data_out ? "hi" : "lo", + (mode < 0) ? "unknown" : modes[mode]); + } else { + int irq = chip->to_irq(chip, offset); + const int pullidx = pull ? 1 : 0; + int val; + static const char * const pulls[] = { + "none ", + "pull enabled", + }; + + seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s", + gpio, + label ?: "(none)", + pulls[pullidx], + (mode < 0) ? "unknown" : modes[mode]); + + val = nmk_gpio_get_input(chip, offset); + seq_printf(s, " VAL %d", val); + + /* + * This races with request_irq(), set_irq_type(), + * and set_irq_wake() ... but those are "rare". + */ + if (irq > 0 && irq_has_action(irq)) { + char *trigger; + bool wake; + + if (nmk_chip->edge_rising & BIT(offset)) + trigger = "edge-rising"; + else if (nmk_chip->edge_falling & BIT(offset)) + trigger = "edge-falling"; + else + trigger = "edge-undefined"; + + wake = !!(nmk_chip->real_wake & BIT(offset)); + + seq_printf(s, " irq-%d %s%s", + irq, trigger, wake ? " wakeup" : ""); + } + } + clk_disable(nmk_chip->clk); +} + +static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned int i, gpio = chip->base; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + nmk_gpio_dbg_show_one(s, NULL, chip, i, gpio); + seq_puts(s, "\n"); + } +} + +#else + +#define nmk_gpio_dbg_show NULL + +#endif + +/* + * We will allocate memory for the state container using devm* allocators + * binding to the first device reaching this point, it doesn't matter if + * it is the pin controller or GPIO driver. However we need to use the right + * platform device when looking up resources so pay attention to pdev. + */ +struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, + struct platform_device *pdev) +{ + struct nmk_gpio_chip *nmk_chip; + struct platform_device *gpio_pdev; + struct device *dev = &pdev->dev; + struct reset_control *reset; + struct device *gpio_dev; + struct gpio_chip *chip; + struct resource *res; + struct clk *clk; + void __iomem *base; + u32 id, ngpio; + int ret; + + gpio_dev = bus_find_device_by_fwnode(&platform_bus_type, fwnode); + if (!gpio_dev) { + dev_err(dev, "populate \"%pfwP\": device not found\n", fwnode); + return ERR_PTR(-ENODEV); + } + gpio_pdev = to_platform_device(gpio_dev); + + if (device_property_read_u32(gpio_dev, "gpio-bank", &id)) { + dev_err(dev, "populate: gpio-bank property not found\n"); + platform_device_put(gpio_pdev); |
