|  | // 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/consumer.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/string_choices.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 int 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); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); | 
|  | #ifdef CONFIG_PINCTRL_NOMADIK | 
|  | struct gpio_desc *desc; | 
|  | #endif | 
|  | 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) { | 
|  | desc = gpio_device_get_desc(chip->gpiodev, offset); | 
|  | mode = nmk_prcm_gpiocr_get_mode(pctldev, desc_to_gpio(desc)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (is_out) { | 
|  | seq_printf(s, " gpio-%-3d (%-20.20s) out %s           %s", | 
|  | offset, label ?: "(none)", str_hi_lo(data_out), | 
|  | (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", | 
|  | offset, 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; | 
|  |  | 
|  | for (i = 0; i < chip->ngpio; i++) { | 
|  | nmk_gpio_dbg_show_one(s, NULL, chip, i); | 
|  | 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); | 
|  | return ERR_PTR(-EINVAL); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PINCTRL_NOMADIK | 
|  | if (id >= ARRAY_SIZE(nmk_gpio_chips)) { | 
|  | dev_err(dev, "populate: invalid id: %u\n", id); | 
|  | platform_device_put(gpio_pdev); | 
|  | return ERR_PTR(-EINVAL); | 
|  | } | 
|  | /* Already populated? */ | 
|  | nmk_chip = nmk_gpio_chips[id]; | 
|  | if (nmk_chip) { | 
|  | platform_device_put(gpio_pdev); | 
|  | return nmk_chip; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | nmk_chip = devm_kzalloc(dev, sizeof(*nmk_chip), GFP_KERNEL); | 
|  | if (!nmk_chip) { | 
|  | platform_device_put(gpio_pdev); | 
|  | return ERR_PTR(-ENOMEM); | 
|  | } | 
|  |  | 
|  | if (device_property_read_u32(gpio_dev, "ngpios", &ngpio)) { | 
|  | ngpio = NMK_GPIO_PER_CHIP; | 
|  | dev_dbg(dev, "populate: using default ngpio (%u)\n", ngpio); | 
|  | } | 
|  |  | 
|  | nmk_chip->is_mobileye_soc = device_is_compatible(gpio_dev, | 
|  | "mobileye,eyeq5-gpio"); | 
|  | nmk_chip->bank = id; | 
|  | chip = &nmk_chip->chip; | 
|  | chip->base = -1; | 
|  | chip->ngpio = ngpio; | 
|  | chip->label = dev_name(gpio_dev); | 
|  | chip->parent = gpio_dev; | 
|  |  | 
|  | /* NOTE: different devices! No devm_platform_ioremap_resource() here! */ | 
|  | res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0); | 
|  | base = devm_ioremap_resource(dev, res); | 
|  | if (IS_ERR(base)) { | 
|  | platform_device_put(gpio_pdev); | 
|  | return ERR_CAST(base); | 
|  | } | 
|  | nmk_chip->addr = base; | 
|  |  | 
|  | /* NOTE: do not use devm_ here! */ | 
|  | clk = clk_get_optional(gpio_dev, NULL); | 
|  | if (IS_ERR(clk)) { | 
|  | platform_device_put(gpio_pdev); | 
|  | return ERR_CAST(clk); | 
|  | } | 
|  | clk_prepare(clk); | 
|  | nmk_chip->clk = clk; | 
|  |  | 
|  | /* NOTE: do not use devm_ here! */ | 
|  | reset = reset_control_get_optional_shared(gpio_dev, NULL); | 
|  | if (IS_ERR(reset)) { | 
|  | clk_unprepare(clk); | 
|  | clk_put(clk); | 
|  | platform_device_put(gpio_pdev); | 
|  | dev_err(dev, "failed getting reset control: %pe\n", | 
|  | reset); | 
|  | return ERR_CAST(reset); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Reset might be shared and asserts/deasserts calls are unbalanced. We | 
|  | * only support sharing this reset with other gpio-nomadik devices that | 
|  | * use this reset to ensure deassertion at probe. | 
|  | */ | 
|  | ret = reset_control_deassert(reset); | 
|  | if (ret) { | 
|  | reset_control_put(reset); | 
|  | clk_unprepare(clk); | 
|  | clk_put(clk); | 
|  | platform_device_put(gpio_pdev); | 
|  | dev_err(dev, "failed reset deassert: %d\n", ret); | 
|  | return ERR_PTR(ret); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PINCTRL_NOMADIK | 
|  | nmk_gpio_chips[id] = nmk_chip; | 
|  | #endif | 
|  | return nmk_chip; | 
|  | } | 
|  |  | 
|  | static void nmk_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) | 
|  | { | 
|  | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | 
|  | struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); | 
|  |  | 
|  | seq_printf(p, "nmk%u-%u-%u", nmk_chip->bank, | 
|  | gc->base, gc->base + gc->ngpio - 1); | 
|  | } | 
|  |  | 
|  | static const struct irq_chip nmk_irq_chip = { | 
|  | .irq_ack = nmk_gpio_irq_ack, | 
|  | .irq_mask = nmk_gpio_irq_mask, | 
|  | .irq_unmask = nmk_gpio_irq_unmask, | 
|  | .irq_set_type = nmk_gpio_irq_set_type, | 
|  | .irq_set_wake = nmk_gpio_irq_set_wake, | 
|  | .irq_startup = nmk_gpio_irq_startup, | 
|  | .irq_shutdown = nmk_gpio_irq_shutdown, | 
|  | .irq_print_chip = nmk_gpio_irq_print_chip, | 
|  | .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, | 
|  | GPIOCHIP_IRQ_RESOURCE_HELPERS, | 
|  | }; | 
|  |  | 
|  | static int nmk_gpio_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device *dev = &pdev->dev; | 
|  | struct nmk_gpio_chip *nmk_chip; | 
|  | struct gpio_irq_chip *girq; | 
|  | bool supports_sleepmode; | 
|  | struct gpio_chip *chip; | 
|  | int irq; | 
|  | int ret; | 
|  |  | 
|  | nmk_chip = nmk_gpio_populate_chip(dev_fwnode(dev), pdev); | 
|  | if (IS_ERR(nmk_chip)) { | 
|  | dev_err(dev, "could not populate nmk chip struct\n"); | 
|  | return PTR_ERR(nmk_chip); | 
|  | } | 
|  |  | 
|  | supports_sleepmode = | 
|  | device_property_read_bool(dev, "st,supports-sleepmode"); | 
|  |  | 
|  | /* Correct platform device ID */ | 
|  | pdev->id = nmk_chip->bank; | 
|  |  | 
|  | irq = platform_get_irq(pdev, 0); | 
|  | if (irq < 0) | 
|  | return irq; | 
|  |  | 
|  | /* | 
|  | * The virt address in nmk_chip->addr is in the nomadik register space, | 
|  | * so we can simply convert the resource address, without remapping | 
|  | */ | 
|  | nmk_chip->sleepmode = supports_sleepmode; | 
|  | spin_lock_init(&nmk_chip->lock); | 
|  |  | 
|  | chip = &nmk_chip->chip; | 
|  | chip->parent = dev; | 
|  | chip->request = gpiochip_generic_request; | 
|  | chip->free = gpiochip_generic_free; | 
|  | chip->get_direction = nmk_gpio_get_dir; | 
|  | chip->direction_input = nmk_gpio_make_input; | 
|  | chip->get = nmk_gpio_get_input; | 
|  | chip->direction_output = nmk_gpio_make_output; | 
|  | chip->set = nmk_gpio_set_output; | 
|  | chip->dbg_show = nmk_gpio_dbg_show; | 
|  | chip->can_sleep = false; | 
|  | chip->owner = THIS_MODULE; | 
|  |  | 
|  | girq = &chip->irq; | 
|  | gpio_irq_chip_set_chip(girq, &nmk_irq_chip); | 
|  | girq->parent_handler = NULL; | 
|  | girq->num_parents = 0; | 
|  | girq->parents = NULL; | 
|  | girq->default_type = IRQ_TYPE_NONE; | 
|  | girq->handler = handle_edge_irq; | 
|  |  | 
|  | ret = devm_request_irq(dev, irq, nmk_gpio_irq_handler, IRQF_SHARED, | 
|  | dev_name(dev), nmk_chip); | 
|  | if (ret) { | 
|  | dev_err(dev, "failed requesting IRQ\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (!nmk_chip->is_mobileye_soc) { | 
|  | clk_enable(nmk_chip->clk); | 
|  | nmk_chip->lowemi = readl_relaxed(nmk_chip->addr + NMK_GPIO_LOWEMI); | 
|  | clk_disable(nmk_chip->clk); | 
|  | } | 
|  |  | 
|  | ret = gpiochip_add_data(chip, nmk_chip); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | platform_set_drvdata(pdev, nmk_chip); | 
|  |  | 
|  | dev_info(dev, "chip registered\n"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id nmk_gpio_match[] = { | 
|  | { .compatible = "st,nomadik-gpio", }, | 
|  | { .compatible = "mobileye,eyeq5-gpio", }, | 
|  | {} | 
|  | }; | 
|  |  | 
|  | static struct platform_driver nmk_gpio_driver = { | 
|  | .driver = { | 
|  | .name = "nomadik-gpio", | 
|  | .of_match_table = nmk_gpio_match, | 
|  | .suppress_bind_attrs = true, | 
|  | }, | 
|  | .probe = nmk_gpio_probe, | 
|  | }; | 
|  |  | 
|  | static int __init nmk_gpio_init(void) | 
|  | { | 
|  | return platform_driver_register(&nmk_gpio_driver); | 
|  | } | 
|  | subsys_initcall(nmk_gpio_init); |