diff options
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r-- | drivers/pinctrl/Kconfig | 5 | ||||
-rw-r--r-- | drivers/pinctrl/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/pinconf.c | 4 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-coh901.c | 3 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-exynos.c | 139 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-exynos.h | 1 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-s3c24xx.c | 651 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-samsung.c | 164 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-samsung.h | 13 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-sunxi.c | 7 | ||||
-rw-r--r-- | drivers/pinctrl/vt8500/pinctrl-wmt.c | 3 |
11 files changed, 981 insertions, 10 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 8f6692438149..526a5d04c216 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -216,6 +216,11 @@ config PINCTRL_EXYNOS5440 select PINMUX select PINCONF +config PINCTRL_S3C24XX + bool "Samsung S3C24XX SoC pinctrl driver" + depends on ARCH_S3C24XX + select PINCTRL_SAMSUNG + config PINCTRL_S3C64XX bool "Samsung S3C64XX SoC pinctrl driver" depends on ARCH_S3C64XX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 9bdaeb8785ce..f90b645fb601 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o +obj-$(CONFIG_PINCTRL_S3C24XX) += pinctrl-s3c24xx.o obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index c67c37e23dd7..694c3ace4520 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -610,7 +610,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d) bool found = false; unsigned long config; - mutex_lock(&pctldev->mutex); + mutex_lock(&pinctrl_maps_mutex); /* Parse the pinctrl map and look for the elected pin/state */ for_each_maps(maps_node, i, map) { @@ -659,7 +659,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d) confops->pin_config_config_dbg_show(pctldev, s, config); exit: - mutex_unlock(&pctldev->mutex); + mutex_unlock(&pinctrl_maps_mutex); return 0; } diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index a67af419f531..d6b41747d687 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -830,7 +830,8 @@ static int __init u300_gpio_probe(struct platform_device *pdev) return 0; err_no_range: - err = gpiochip_remove(&gpio->chip); + if (gpiochip_remove(&gpio->chip)) + dev_err(&pdev->dev, "failed to remove gpio chip\n"); err_no_chip: err_no_domain: err_no_port: diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index ac742817ebce..2d76f66a2e0b 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -196,6 +196,12 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) return IRQ_HANDLED; } +struct exynos_eint_gpio_save { + u32 eint_con; + u32 eint_fltcon0; + u32 eint_fltcon1; +}; + /* * exynos_eint_gpio_init() - setup handling of external gpio interrupts. * @d: driver data of samsung pinctrl driver. @@ -204,8 +210,8 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) { struct samsung_pin_bank *bank; struct device *dev = d->dev; - unsigned int ret; - unsigned int i; + int ret; + int i; if (!d->irq) { dev_err(dev, "irq number not available\n"); @@ -227,11 +233,29 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) bank->nr_pins, &exynos_gpio_irqd_ops, bank); if (!bank->irq_domain) { dev_err(dev, "gpio irq domain add failed\n"); - return -ENXIO; + ret = -ENXIO; + goto err_domains; + } + + bank->soc_priv = devm_kzalloc(d->dev, + sizeof(struct exynos_eint_gpio_save), GFP_KERNEL); + if (!bank->soc_priv) { + irq_domain_remove(bank->irq_domain); + ret = -ENOMEM; + goto err_domains; } } return 0; + +err_domains: + for (--i, --bank; i >= 0; --i, --bank) { + if (bank->eint_type != EINT_TYPE_GPIO) + continue; + irq_domain_remove(bank->irq_domain); + } + + return ret; } static void exynos_wkup_irq_unmask(struct irq_data *irqd) @@ -326,6 +350,28 @@ static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) return 0; } +static u32 exynos_eint_wake_mask = 0xffffffff; + +u32 exynos_get_eint_wake_mask(void) +{ + return exynos_eint_wake_mask; +} + +static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq); + + pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq); + + if (!on) + exynos_eint_wake_mask |= bit; + else + exynos_eint_wake_mask &= ~bit; + + return 0; +} + /* * irq_chip for wakeup interrupts */ @@ -335,6 +381,7 @@ static struct irq_chip exynos_wkup_irq_chip = { .irq_mask = exynos_wkup_irq_mask, .irq_ack = exynos_wkup_irq_ack, .irq_set_type = exynos_wkup_irq_set_type, + .irq_set_wake = exynos_wkup_irq_set_wake, }; /* interrupt handler for wakeup interrupts 0..15 */ @@ -505,6 +552,72 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) return 0; } +static void exynos_pinctrl_suspend_bank( + struct samsung_pinctrl_drv_data *drvdata, + struct samsung_pin_bank *bank) +{ + struct exynos_eint_gpio_save *save = bank->soc_priv; + void __iomem *regs = drvdata->virt_base; + + save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET + + bank->eint_offset); + save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET + + 2 * bank->eint_offset); + save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET + + 2 * bank->eint_offset + 4); + + pr_debug("%s: save con %#010x\n", bank->name, save->eint_con); + pr_debug("%s: save fltcon0 %#010x\n", bank->name, save->eint_fltcon0); + pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1); +} + +static void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata) +{ + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + struct samsung_pin_bank *bank = ctrl->pin_banks; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) + if (bank->eint_type == EINT_TYPE_GPIO) + exynos_pinctrl_suspend_bank(drvdata, bank); +} + +static void exynos_pinctrl_resume_bank( + struct samsung_pinctrl_drv_data *drvdata, + struct samsung_pin_bank *bank) +{ + struct exynos_eint_gpio_save *save = bank->soc_priv; + void __iomem *regs = drvdata->virt_base; + + pr_debug("%s: con %#010x => %#010x\n", bank->name, + readl(regs + EXYNOS_GPIO_ECON_OFFSET + + bank->eint_offset), save->eint_con); + pr_debug("%s: fltcon0 %#010x => %#010x\n", bank->name, + readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET + + 2 * bank->eint_offset), save->eint_fltcon0); + pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name, + readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET + + 2 * bank->eint_offset + 4), save->eint_fltcon1); + + writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET + + bank->eint_offset); + writel(save->eint_fltcon0, regs + EXYNOS_GPIO_EFLTCON_OFFSET + + 2 * bank->eint_offset); + writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET + + 2 * bank->eint_offset + 4); +} + +static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata) +{ + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + struct samsung_pin_bank *bank = ctrl->pin_banks; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) + if (bank->eint_type == EINT_TYPE_GPIO) + exynos_pinctrl_resume_bank(drvdata, bank); +} + /* pin banks of exynos4210 pin-controller 0 */ static struct samsung_pin_bank exynos4210_pin_banks0[] = { EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00), @@ -568,6 +681,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos4210-gpio-ctrl0", }, { /* pin-controller instance 1 data */ @@ -582,6 +697,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos4210-gpio-ctrl1", }, { /* pin-controller instance 2 data */ @@ -663,6 +780,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos4x12-gpio-ctrl0", }, { /* pin-controller instance 1 data */ @@ -677,6 +796,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos4x12-gpio-ctrl1", }, { /* pin-controller instance 2 data */ @@ -687,6 +808,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos4x12-gpio-ctrl2", }, { /* pin-controller instance 3 data */ @@ -697,6 +820,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos4x12-gpio-ctrl3", }, }; @@ -775,6 +900,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos5250-gpio-ctrl0", }, { /* pin-controller instance 1 data */ @@ -785,6 +912,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos5250-gpio-ctrl1", }, { /* pin-controller instance 2 data */ @@ -795,6 +924,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos5250-gpio-ctrl2", }, { /* pin-controller instance 3 data */ @@ -805,6 +936,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, .svc = EXYNOS_SVC_OFFSET, .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, .label = "exynos5250-gpio-ctrl3", }, }; diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 9b1f77a5bf0f..3c91c357792f 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h @@ -19,6 +19,7 @@ /* External GPIO and wakeup interrupt related definitions */ #define EXYNOS_GPIO_ECON_OFFSET 0x700 +#define EXYNOS_GPIO_EFLTCON_OFFSET 0x800 #define EXYNOS_GPIO_EMASK_OFFSET 0x900 #define EXYNOS_GPIO_EPEND_OFFSET 0xA00 #define EXYNOS_WKUP_ECON_OFFSET 0xE00 diff --git a/drivers/pinctrl/pinctrl-s3c24xx.c b/drivers/pinctrl/pinctrl-s3c24xx.c new file mode 100644 index 000000000000..24446daaad7d --- /dev/null +++ b/drivers/pinctrl/pinctrl-s3c24xx.c @@ -0,0 +1,651 @@ +/* + * S3C24XX specific support for Samsung pinctrl/gpiolib driver. + * + * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This file contains the SamsungS3C24XX specific information required by the + * Samsung pinctrl/gpiolib driver. It also includes the implementation of + * external gpio and wakeup interrupt support. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/of_irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/err.h> + +#include "pinctrl-samsung.h" + +#define NUM_EINT 24 +#define NUM_EINT_IRQ 6 +#define EINT_MAX_PER_GROUP 8 + +#define EINTPEND_REG 0xa8 +#define EINTMASK_REG 0xa4 + +#define EINT_GROUP(i) ((int)((i) / EINT_MAX_PER_GROUP)) +#define EINT_REG(i) ((EINT_GROUP(i) * 4) + 0x88) +#define EINT_OFFS(i) ((i) % EINT_MAX_PER_GROUP * 4) + +#define EINT_LEVEL_LOW 0 +#define EINT_LEVEL_HIGH 1 +#define EINT_EDGE_FALLING 2 +#define EINT_EDGE_RISING 4 +#define EINT_EDGE_BOTH 6 +#define EINT_MASK 0xf + +static struct samsung_pin_bank_type bank_type_1bit = { + .fld_width = { 1, 1, }, + .reg_offset = { 0x00, 0x04, }, +}; + +static struct samsung_pin_bank_type bank_type_2bit = { + .fld_width = { 2, 1, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, }, +}; + +#define PIN_BANK_A(pins, reg, id) \ + { \ + .type = &bank_type_1bit, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_NONE, \ + .name = id \ + } + +#define PIN_BANK_2BIT(pins, reg, id) \ + { \ + .type = &bank_type_2bit, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_NONE, \ + .name = id \ + } + +#define PIN_BANK_2BIT_EINTW(pins, reg, id, eoffs, emask)\ + { \ + .type = &bank_type_2bit, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_WKUP, \ + .eint_func = 2, \ + .eint_mask = emask, \ + .eint_offset = eoffs, \ + .name = id \ + } + +/** + * struct s3c24xx_eint_data: EINT common data + * @drvdata: pin controller driver data + * @domains: IRQ domains of particular EINT interrupts + * @parents: mapped parent irqs in the main interrupt controller + */ +struct s3c24xx_eint_data { + struct samsung_pinctrl_drv_data *drvdata; + struct irq_domain *domains[NUM_EINT]; + int parents[NUM_EINT_IRQ]; +}; + +/** + * struct s3c24xx_eint_domain_data: per irq-domain data + * @bank: pin bank related to the domain + * @eint_data: common data + * eint0_3_parent_only: live eints 0-3 only in the main intc + */ +struct s3c24xx_eint_domain_data { + struct samsung_pin_bank *bank; + struct s3c24xx_eint_data *eint_data; + bool eint0_3_parent_only; +}; + +static int s3c24xx_eint_get_trigger(unsigned int type) +{ + switch (type) { + case IRQ_TYPE_EDGE_RISING: + return EINT_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + return EINT_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + return EINT_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + return EINT_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + return EINT_LEVEL_LOW; + break; + default: + return -EINVAL; + } +} + +static void s3c24xx_eint_set_handler(unsigned int irq, unsigned int type) +{ + /* Edge- and level-triggered interrupts need different handlers */ + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(irq, handle_edge_irq); + else + __irq_set_handler_locked(irq, handle_level_irq); +} + +static void s3c24xx_eint_set_function(struct samsung_pinctrl_drv_data *d, + struct samsung_pin_bank *bank, int pin) +{ + struct samsung_pin_bank_type *bank_type = bank->type; + unsigned long flags; + void __iomem *reg; + u8 shift; + u32 mask; + u32 val; + + /* Make sure that pin is configured as interrupt */ + reg = d->virt_base + bank->pctl_offset; + shift = pin * bank_type->fld_width[PINCFG_TYPE_FUNC]; + mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; + + spin_lock_irqsave(&bank->slock, flags); + + val = readl(reg); + val &= ~(mask << shift); + val |= bank->eint_func << shift; + writel(val, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +static int s3c24xx_eint_type(struct irq_data *data, unsigned int type) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + int index = bank->eint_offset + data->hwirq; + void __iomem *reg; + int trigger; + u8 shift; + u32 val; + + trigger = s3c24xx_eint_get_trigger(type); + if (trigger < 0) { + dev_err(d->dev, "unsupported external interrupt type\n"); + return -EINVAL; + } + + s3c24xx_eint_set_handler(data->irq, type); + + /* Set up interrupt trigger */ + reg = d->virt_base + EINT_REG(index); + shift = EINT_OFFS(index); + + val = readl(reg); + val &= ~(EINT_MASK << shift); + val |= trigger << shift; + writel(val, reg); + + s3c24xx_eint_set_function(d, bank, data->hwirq); + + return 0; +} + +/* Handling of EINTs 0-3 on all except S3C2412 and S3C2413 */ + +static void s3c2410_eint0_3_ack(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct s3c24xx_eint_domain_data *ddata = bank->irq_domain->host_data; + struct s3c24xx_eint_data *eint_data = ddata->eint_data; + int parent_irq = eint_data->parents[data->hwirq]; + struct irq_chip *parent_chip = irq_get_chip(parent_irq); + + parent_chip->irq_ack(irq_get_irq_data(parent_irq)); +} + +static void s3c2410_eint0_3_mask(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct s3c24xx_eint_domain_data *ddata = bank->irq_domain->host_data; + struct s3c24xx_eint_data *eint_data = ddata->eint_data; + int parent_irq = eint_data->parents[data->hwirq]; + struct irq_chip *parent_chip = irq_get_chip(parent_irq); + + parent_chip->irq_mask(irq_get_irq_data(parent_irq)); +} + +static void s3c2410_eint0_3_unmask(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct s3c24xx_eint_domain_data *ddata = bank->irq_domain->host_data; + struct s3c24xx_eint_data *eint_data = ddata->eint_data; + int parent_irq = eint_data->parents[data->hwirq]; + struct irq_chip *parent_chip = irq_get_chip(parent_irq); + + parent_chip->irq_unmask(irq_get_irq_data(parent_irq)); +} + +static struct irq_chip s3c2410_eint0_3_chip = { + .name = "s3c2410-eint0_3", + .irq_ack = s3c2410_eint0_3_ack, + .irq_mask = s3c2410_eint0_3_mask, + .irq_unmask = s3c2410_eint0_3_unmask, + .irq_set_type = s3c24xx_eint_type, +}; + +static void s3c2410_demux_eint0_3(unsigned int irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct s3c24xx_eint_data *eint_data = irq_get_handler_data(irq); + unsigned int virq; + + /* the first 4 eints have a simple 1 to 1 mapping */ + virq = irq_linear_revmap(eint_data->domains[data->hwirq], data->hwirq); + /* Something must be really wrong if an unmapped EINT is unmasked */ + BUG_ON(!virq); + + generic_handle_irq(virq); +} + +/* Handling of EINTs 0-3 on S3C2412 and S3C2413 */ + +static void s3c2412_eint0_3_ack(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + + unsigned long bitval = 1UL << data->hwirq; + writel(bitval, d->virt_base + EINTPEND_REG); +} + +static void s3c2412_eint0_3_mask(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned long mask; + + mask = readl(d->virt_base + EINTMASK_REG); + mask |= (1UL << data->hwirq); + writel(mask, d->virt_base + EINTMASK_REG); +} + +static void s3c2412_eint0_3_unmask(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned long mask; + + mask = readl(d->virt_base + EINTMASK_REG); + mask &= ~(1UL << data->hwirq); + writel(mask, d->virt_base + EINTMASK_REG); +} + +static struct irq_chip s3c2412_eint0_3_chip = { + .name = "s3c2412-eint0_3", + .irq_ack = s3c2412_eint0_3_ack, + .irq_mask = s3c2412_eint0_3_mask, + .irq_unmask = s3c2412_eint0_3_unmask, + .irq_set_type = s3c24xx_eint_type, +}; + +static void s3c2412_demux_eint0_3(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct irq_data *data = irq_desc_get_irq_data(desc); + struct s3c24xx_eint_data *eint_data = irq_get_handler_data(irq); + unsigned int virq; + + chained_irq_enter(chip, desc); + + /* the first 4 eints have a simple 1 to 1 mapping */ + virq = irq_linear_revmap(eint_data->domains[data->hwirq], data->hwirq); + /* Something must be really wrong if an unmapped EINT is unmasked */ + BUG_ON(!virq); + + generic_handle_irq(virq); + + chained_irq_exit(chip, desc); +} + +/* Handling of all other eints */ + +static void s3c24xx_eint_ack(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned char index = bank->eint_offset + data->hwirq; + + writel(1UL << index, d->virt_base + EINTPEND_REG); +} + +static void s3c24xx_eint_mask(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned char index = bank->eint_offset + data->hwirq; + unsigned long mask; + + mask = readl(d->virt_base + EINTMASK_REG); + mask |= (1UL << index); + writel(mask, d->virt_base + EINTMASK_REG); +} + +static void s3c24xx_eint_unmask(struct irq_data *data) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(data); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + unsigned char index = bank->eint_offset + data->hwirq; + unsigned long mask; + + mask = readl(d->virt_base + EINTMASK_REG); + mask &= ~(1UL << index); + writel(mask, d->virt_base + EINTMASK_REG); +} + +static struct irq_chip s3c24xx_eint_chip = { + .name = "s3c-eint", + .irq_ack = s3c24xx_eint_ack, + .irq_mask = s3c24xx_eint_mask, + .irq_unmask = s3c24xx_eint_unmask, + .irq_set_type = s3c24xx_eint_type, +}; + +static inline void s3c24xx_demux_eint(unsigned int irq, struct irq_desc *desc, + u32 offset, u32 range) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct s3c24xx_eint_data *data = irq_get_handler_data(irq); + struct samsung_pinctrl_drv_data *d = data->drvdata; + unsigned int pend, mask; + + chained_irq_enter(chip, desc); + + pend = readl(d->virt_base + EINTPEND_REG); + mask = readl(d->virt_base + EINTMASK_REG); + + pend &= ~mask; + pend &= range; + + while (pend) { + unsigned int virq; + + irq = __ffs(pend); + pend &= ~(1 << irq); + virq = irq_linear_revmap(data->domains[irq], irq - offset); + /* Something is really wrong if an unmapped EINT is unmasked */ + BUG_ON(!virq); + + generic_handle_irq(virq); + } + + chained_irq_exit(chip, desc); +} + +static void s3c24xx_demux_eint4_7(unsigned int irq, struct irq_desc *desc) +{ + s3c24xx_demux_eint(irq, desc, 0, 0xf0); +} + +static void s3c24xx_demux_eint8_23(unsigned int irq, struct irq_desc *desc) +{ + s3c24xx_demux_eint(irq, desc, 8, 0xffff00); +} + +static irq_flow_handler_t s3c2410_eint_handlers[NUM_EINT_IRQ] = { + s3c2410_demux_eint0_3, + s3c2410_demux_eint0_3, + s3c2410_demux_eint0_3, + s3c2410_demux_eint0_3, + s3c24xx_demux_eint4_7, + s3c24xx_demux_eint8_23, +}; + +static irq_flow_handler_t s3c2412_eint_handlers[NUM_EINT_IRQ] = { + s3c2412_demux_eint0_3, + s3c2412_demux_eint0_3, + s3c2412_demux_eint0_3, + s3c2412_demux_eint0_3, + s3c24xx_demux_eint4_7, + s3c24xx_demux_eint8_23, +}; + +static int s3c24xx_gpf_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct s3c24xx_eint_domain_data *ddata = h->host_data; + struct samsung_pin_bank *bank = ddata->bank; + + if (!(bank->eint_mask & (1 << (bank->eint_offset + hw)))) + return -EINVAL; + + if (hw <= 3) { + if (ddata->eint0_3_parent_only) + irq_set_chip_and_handler(virq, &s3c2410_eint0_3_chip, + handle_edge_irq); + else + irq_set_chip_and_handler(virq, &s3c2412_eint0_3_chip, + handle_edge_irq); + } else { + irq_set_chip_and_handler(virq, &s3c24xx_eint_chip, + handle_edge_irq); + } + irq_set_chip_data(virq, bank); + set_irq_flags(virq, IRQF_VALID); + return 0; +} + +static const struct irq_domain_ops s3c24xx_gpf_irq_ops = { + .map = s3c24xx_gpf_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int s3c24xx_gpg_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct s3c24xx_eint_domain_data *ddata = h->host_data; + struct samsung_pin_bank *bank = ddata->bank; + + if (!(bank->eint_mask & (1 << (bank->eint_offset + hw)))) + return -EINVAL; + + irq_set_chip_and_handler(virq, &s3c24xx_eint_chip, handle_edge_irq); + irq_set_chip_data(virq, bank); + set_irq_flags(virq, IRQF_VALID); + return 0; +} + +static const struct irq_domain_ops s3c24xx_gpg_irq_ops = { + .map = s3c24xx_gpg_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static const struct of_device_id s3c24xx_eint_irq_ids[] = { + { .compatible = "samsung,s3c2410-wakeup-eint", .data = (void *)1 }, + { .compatible = "samsung,s3c2412-wakeup-eint", .data = (void *)0 }, + { } +}; + +static int s3c24xx_eint_init(struct samsung_pinctrl_drv_data *d) +{ + struct device *dev = d->dev; + const struct of_device_id *match; + struct device_node *eint_np = NULL; + struct device_node *np; + struct samsung_pin_bank *bank; + struct s3c24xx_eint_data *eint_data; + const struct irq_domain_ops *ops; + unsigned int i; + bool eint0_3_parent_only; + irq_flow_handler_t *handlers; + + for_each_child_of_node(dev->of_node, np) { + match = of_match_node(s3c24xx_eint_irq_ids, np); + if (match) { + eint_np = np; + eint0_3_parent_only = (bool)match->data; + break; + } + } + if (!eint_np) + return -ENODEV; + + eint_data = devm_kzalloc(dev, sizeof(*eint_data), GFP_KERNEL); + if (!eint_data) + return -ENOMEM; + + eint_data->drvdata = d; + + handlers = eint0_3_parent_only ? s3c2410_eint_handlers + : s3c2412_eint_handlers; + for (i = 0; i < NUM_EINT_IRQ; ++i) { + unsigned int irq; + + irq = irq_of_parse_and_map(eint_np, i); + if (!irq) { + dev_err(dev, "failed to get wakeup EINT IRQ %d\n", i); + return -ENXIO; + } + + eint_data->parents[i] = irq; + irq_set_chained_handler(irq, handlers[i]); + irq_set_handler_data(irq, eint_data); + } + + bank = d->ctrl->pin_banks; + for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { + struct s3c24xx_eint_domain_data *ddata; + unsigned int mask; + unsigned int irq; + unsigned int pin; + + if (bank->eint_type != EINT_TYPE_WKUP) + continue; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + ddata->bank = bank; + ddata->eint_data = eint_data; + ddata->eint0_3_parent_only = eint0_3_parent_only; + + ops = (bank->eint_offset == 0) ? &s3c24xx_gpf_irq_ops + : &s3c24xx_gpg_irq_ops; + + bank->irq_domain = irq_domain_add_linear(bank->of_node, + bank->nr_pins, ops, ddata); + if (!bank->irq_domain) { + dev_err(dev, "wkup irq domain add failed\n"); + return -ENXIO; + } + + irq = bank->eint_offset; + mask = bank->eint_mask; + for (pin = 0; mask; ++pin, mask >>= 1) { + if (irq > NUM_EINT) + break; + if (!(mask & 1)) + continue; + eint_data->domains[irq] = bank->irq_domain; + ++irq; + } + } + + return 0; +} + +static struct samsung_pin_bank s3c2412_pin_banks[] = { + PIN_BANK_A(23, 0x000, "gpa"), + PIN_BANK_2BIT(11, 0x010, "gpb"), + PIN_BANK_2BIT(16, 0x020, "gpc"), + PIN_BANK_2BIT(16, 0x030, "gpd"), + PIN_BANK_2BIT(16, 0x040, "gpe"), + PIN_BANK_2BIT_EINTW(8, 0x050, "gpf", 0, 0xff), + PIN_BANK_2BIT_EINTW(16, 0x060, "gpg", 8, 0xffff00), + PIN_BANK_2BIT(11, 0x070, "gph"), + PIN_BANK_2BIT(13, 0x080, "gpj"), +}; + +struct samsung_pin_ctrl s3c2412_pin_ctrl[] = { + { + .pin_banks = s3c2412_pin_banks, + .nr_banks = ARRAY_SIZE(s3c2412_pin_banks), + .eint_wkup_init = s3c24xx_eint_init, + .label = "S3C2412-GPIO", + }, +}; + +static struct samsung_pin_bank s3c2416_pin_banks[] = { + PIN_BANK_A(27, 0x000, "gpa"), + PIN_BANK_2BIT(11, 0x010, "gpb"), + PIN_BANK_2BIT(16, 0x020, "gpc"), + PIN_BANK_2BIT(16, 0x030, "gpd"), + PIN_BANK_2BIT(16, 0x040, "gpe"), + PIN_BANK_2BIT_EINTW(8, 0x050, "gpf", 0, 0xff), + PIN_BANK_2BIT_EINTW(8, 0x060, "gpg", 8, 0xff00), + PIN_BANK_2BIT(15, 0x070, "gph"), + PIN_BANK_2BIT(16, 0x0e0, "gpk"), + PIN_BANK_2BIT(14, 0x0f0, "gpl"), + PIN_BANK_2BIT(2, 0x100, "gpm"), +}; + +struct samsung_pin_ctrl s3c2416_pin_ctrl[] = { + { + .pin_banks = s3c2416_pin_banks, + .nr_banks = ARRAY_SIZE(s3c2416_pin_banks), + .eint_wkup_init = s3c24xx_eint_init, + .label = "S3C2416-GPIO", + }, +}; + +static struct samsung_pin_bank s3c2440_pin_banks[] = { + PIN_BANK_A(25, 0x000, "gpa"), + PIN_BANK_2BIT(11, 0x010, "gpb"), + PIN_BANK_2BIT(16, 0x020, "gpc"), + PIN_BANK_2BIT(16, 0x030, "gpd"), + PIN_BANK_2BIT(16, 0x040, "gpe"), + PIN_BANK_2BIT_EINTW(8, 0x050, "gpf", 0, 0xff), + PIN_BANK_2BIT_EINTW(16, 0x060, "gpg", 8, 0xffff00), + PIN_BANK_2BIT(11, 0x070, "gph"), + PIN_BANK_2BIT(13, 0x0d0, "gpj"), +}; + +struct samsung_pin_ctrl s3c2440_pin_ctrl[] = { + { + .pin_banks = s3c2440_pin_banks, + .nr_banks = ARRAY_SIZE(s3c2440_pin_banks), + .eint_wkup_init = s3c24xx_eint_init, + .label = "S3C2440-GPIO", + }, +}; + +static struct samsung_pin_bank s3c2450_pin_banks[] = { + PIN_BANK_A(28, 0x000, "gpa"), + PIN_BANK_2BIT(11, 0x010, "gpb"), + PIN_BANK_2BIT(16, 0x020, "gpc"), + PIN_BANK_2BIT(16, 0x030, "gpd"), + PIN_BANK_2BIT(16, 0x040, "gpe"), + PIN_BANK_2BIT_EINTW(8, 0x050, "gpf", 0, 0xff), + PIN_BANK_2BIT_EINTW(16, 0x060, "gpg", 8, 0xffff00), + PIN_BANK_2BIT(15, 0x070, "gph"), + PIN_BANK_2BIT(16, 0x0d0, "gpj"), + PIN_BANK_2BIT(16, 0x0e0, "gpk"), + PIN_BANK_2BIT(15, 0x0f0, "gpl"), + PIN_BANK_2BIT(2, 0x100, "gpm"), +}; + +struct samsung_pin_ctrl s3c2450_pin_ctrl[] = { + { + .pin_banks = s3c2450_pin_banks, + .nr_banks = ARRAY_SIZE(s3c2450_pin_banks), + .eint_wkup_init = s3c24xx_eint_init, + .label = "S3C2450-GPIO", + }, +}; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 055d0162098b..e67ff1b8042c 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -28,6 +28,7 @@ #include <linux/gpio.h> #include <linux/irqdomain.h> #include <linux/spinlock.h> +#include <linux/syscore_ops.h> #include "core.h" #include "pinctrl-samsung.h" @@ -48,6 +49,9 @@ static struct pin_config { { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN }, }; +/* Global list of devices (struct samsung_pinctrl_drv_data) */ +LIST_HEAD(drvdata_list); + static unsigned int pin_base; static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) @@ -956,9 +960,151 @@ static int samsung_pinctrl_probe(struct platform_device *pdev) ctrl->eint_wkup_init(drvdata); platform_set_drvdata(pdev, drvdata); + + /* Add to the global list */ + list_add_tail(&drvdata->node, &drvdata_list); + + return 0; +} + +#ifdef CONFIG_PM + +/** + * samsung_pinctrl_suspend_dev - save pinctrl state for suspend for a device + * + * Save data for all banks handled by this device. + */ +static void samsung_pinctrl_suspend_dev( + struct samsung_pinctrl_drv_data *drvdata) +{ + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + void __iomem *virt_base = drvdata->virt_base; + int i; + + for (i = 0; i < ctrl->nr_banks; i++) { + struct samsung_pin_bank *bank = &ctrl->pin_banks[i]; + void __iomem *reg = virt_base + bank->pctl_offset; + + u8 *offs = bank->type->reg_offset; + u8 *widths = bank->type->fld_width; + enum pincfg_type type; + + /* Registers without a powerdown config aren't lost */ + if (!widths[PINCFG_TYPE_CON_PDN]) + continue; + + for (type = 0; type < PINCFG_TYPE_NUM; type++) + if (widths[type]) + bank->pm_save[type] = readl(reg + offs[type]); + + if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { + /* Some banks have two config registers */ + bank->pm_save[PINCFG_TYPE_NUM] = + readl(reg + offs[PINCFG_TYPE_FUNC] + 4); + pr_debug("Save %s @ %p (con %#010x %08x)\n", + bank->name, reg, + bank->pm_save[PINCFG_TYPE_FUNC], + bank->pm_save[PINCFG_TYPE_NUM]); + } else { + pr_debug("Save %s @ %p (con %#010x)\n", bank->name, + reg, bank->pm_save[PINCFG_TYPE_FUNC]); + } + } + + if (ctrl->suspend) + ctrl->suspend(drvdata); +} + +/** + * samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device + * + * Restore one of the banks that was saved during suspend. + * + * We don't bother doing anything complicated to avoid glitching lines since + * we're called before pad retention is turned off. + */ +static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata) +{ + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + void __iomem *virt_base = drvdata->virt_base; + int i; + + if (ctrl->resume) + ctrl->resume(drvdata); + + for (i = 0; i < ctrl->nr_banks; i++) { + struct samsung_pin_bank *bank = &ctrl->pin_banks[i]; + void __iomem *reg = virt_base + bank->pctl_offset; + + u8 *offs = bank->type->reg_offset; + u8 *widths = bank->type->fld_width; + enum pincfg_type type; + + /* Registers without a powerdown config aren't lost */ + if (!widths[PINCFG_TYPE_CON_PDN]) + continue; + + if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { + /* Some banks have two config registers */ + pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n", + bank->name, reg, + readl(reg + offs[PINCFG_TYPE_FUNC]), + readl(reg + offs[PINCFG_TYPE_FUNC] + 4), + bank->pm_save[PINCFG_TYPE_FUNC], + bank->pm_save[PINCFG_TYPE_NUM]); + writel(bank->pm_save[PINCFG_TYPE_NUM], + reg + offs[PINCFG_TYPE_FUNC] + 4); + } else { + pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name, + reg, readl(reg + offs[PINCFG_TYPE_FUNC]), + bank->pm_save[PINCFG_TYPE_FUNC]); + } + for (type = 0; type < PINCFG_TYPE_NUM; type++) + if (widths[type]) + writel(bank->pm_save[type], reg + offs[type]); + } +} + +/** + * samsung_pinctrl_suspend - save pinctrl state for suspend + * + * Save data for all banks across all devices. + */ +static int samsung_pinctrl_suspend(void) +{ + struct samsung_pinctrl_drv_data *drvdata; + + list_for_each_entry(drvdata, &drvdata_list, node) { + samsung_pinctrl_suspend_dev(drvdata); + } + return 0; } +/** + * samsung_pinctrl_resume - restore pinctrl state for suspend + * + * Restore data for all banks across all devices. + */ +static void samsung_pinctrl_resume(void) +{ + struct samsung_pinctrl_drv_data *drvdata; + + list_for_each_entry_reverse(drvdata, &drvdata_list, node) { + samsung_pinctrl_resume_dev(drvdata); + } +} + +#else +#define samsung_pinctrl_suspend NULL +#define samsung_pinctrl_resume NULL +#endif + +static struct syscore_ops samsung_pinctrl_syscore_ops = { + .suspend = samsung_pinctrl_suspend, + .resume = samsung_pinctrl_resume, +}; + static const struct of_device_id samsung_pinctrl_dt_match[] = { #ifdef CONFIG_PINCTRL_EXYNOS { .compatible = "samsung,exynos4210-pinctrl", @@ -972,6 +1118,16 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { { .compatible = "samsung,s3c64xx-pinctrl", .data = s3c64xx_pin_ctrl }, #endif +#ifdef CONFIG_PINCTRL_S3C24XX + { .compatible = "samsung,s3c2412-pinctrl", + .data = s3c2412_pin_ctrl }, + { .compatible = "samsung,s3c2416-pinctrl", + .data = s3c2416_pin_ctrl }, + { .compatible = "samsung,s3c2440-pinctrl", + .data = s3c2440_pin_ctrl }, + { .compatible = "samsung,s3c2450-pinctrl", + .data = s3c2450_pin_ctrl }, +#endif {}, }; MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); @@ -987,6 +1143,14 @@ static struct platform_driver samsung_pinctrl_driver = { static int __init samsung_pinctrl_drv_register(void) { + /* + * Register syscore ops for save/restore of registers across suspend. + * It's important to ensure that this driver is running at an earlier + * initcall level than any arch-specific init calls that install syscore + * ops that turn off pad retention (like exynos_pm_resume). + */ + register_syscore_ops(&samsung_pinctrl_syscore_ops); + return platform_driver_register(&samsung_pinctrl_driver); } postcore_initcall(samsung_pinctrl_drv_register); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 7c7f9ebcd05b..79fcc2076c00 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -127,6 +127,7 @@ struct samsung_pin_bank_type { * @gpio_chip: GPIO chip of the bank. * @grange: linux gpio pin range supported by this bank. * @slock: spinlock protecting bank registers + * @pm_save: saved register values during suspend */ struct samsung_pin_bank { struct samsung_pin_bank_type *type; @@ -138,12 +139,15 @@ struct samsung_pin_bank { u32 eint_mask; u32 eint_offset; char *name; + void *soc_priv; struct device_node *of_node; struct samsung_pinctrl_drv_data *drvdata; struct irq_domain *irq_domain; struct gpio_chip gpio_chip; struct pinctrl_gpio_range grange; spinlock_t slock; + + u32 pm_save[PINCFG_TYPE_NUM + 1]; /* +1 to handle double CON registers*/ }; /** @@ -184,11 +188,15 @@ struct samsung_pin_ctrl { int (*eint_gpio_init)(struct samsung_pinctrl_drv_data *); int (*eint_wkup_init)(struct samsung_pinctrl_drv_data *); + void (*suspend)(struct samsung_pinctrl_drv_data *); + void (*resume)(struct samsung_pinctrl_drv_data *); + char *label; }; /** * struct samsung_pinctrl_drv_data: wrapper for holding driver data together. + * @node: global list node * @virt_base: register base address of the controller. * @dev: device instance representing the controller. * @irq: interrpt number used by the controller to notify gpio interrupts. @@ -201,6 +209,7 @@ struct samsung_pin_ctrl { * @nr_function: number of such pin functions. */ struct samsung_pinctrl_drv_data { + struct list_head node; void __iomem *virt_base; struct device *dev; int irq; @@ -246,5 +255,9 @@ extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; extern struct samsung_pin_ctrl exynos5250_pin_ctrl[]; extern struct samsung_pin_ctrl s3c64xx_pin_ctrl[]; +extern struct samsung_pin_ctrl s3c2412_pin_ctrl[]; +extern struct samsung_pin_ctrl s3c2416_pin_ctrl[]; +extern struct samsung_pin_ctrl s3c2440_pin_ctrl[]; +extern struct samsung_pin_ctrl s3c2450_pin_ctrl[]; #endif /* __PINCTRL_SAMSUNG_H */ diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index c52fc2c08732..b7d8c890514c 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -1990,8 +1990,10 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) } clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); goto gpiochip_error; + } clk_prepare_enable(clk); @@ -2000,7 +2002,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) return 0; gpiochip_error: - ret = gpiochip_remove(pctl->chip); + if (gpiochip_remove(pctl->chip)) + dev_err(&pdev->dev, "failed to remove gpio chip\n"); pinctrl_error: pinctrl_unregister(pctl->pctl_dev); return ret; diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c index ab63104e8dc9..70d986e04afb 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wmt.c +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -609,8 +609,7 @@ int wmt_pinctrl_probe(struct platform_device *pdev, return 0; fail_range: - err = gpiochip_remove(&data->gpio_chip); - if (err) + if (gpiochip_remove(&data->gpio_chip)) dev_err(&pdev->dev, "failed to remove gpio chip\n"); fail_gpio: pinctrl_unregister(data->pctl_dev); |