summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/samsung/pinctrl-exynos.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/samsung/pinctrl-exynos.c')
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c530
1 files changed, 435 insertions, 95 deletions
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index a8212fc126bf..81fe0b08a9af 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -13,6 +13,7 @@
// the Samsung pinctrl/gpiolib driver. It also includes the implementation of
// external gpio and wakeup interrupt support.
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
@@ -22,6 +23,7 @@
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string_choices.h>
#include <linux/regmap.h>
#include <linux/err.h>
#include <linux/soc/samsung/exynos-pmu.h>
@@ -30,18 +32,24 @@
#include "pinctrl-samsung.h"
#include "pinctrl-exynos.h"
+#define MAX_WAKEUP_REG 3
+
struct exynos_irq_chip {
struct irq_chip chip;
u32 eint_con;
u32 eint_mask;
u32 eint_pend;
- u32 *eint_wake_mask_value;
+ u32 eint_num_wakeup_reg;
u32 eint_wake_mask_reg;
void (*set_eint_wakeup_mask)(struct samsung_pinctrl_drv_data *drvdata,
struct exynos_irq_chip *irq_chip);
};
+static u32 eint_wake_mask_values[MAX_WAKEUP_REG] = { EXYNOS_EINT_WAKEUP_MASK_DISABLED,
+ EXYNOS_EINT_WAKEUP_MASK_DISABLED,
+ EXYNOS_EINT_WAKEUP_MASK_DISABLED};
+
static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip)
{
return container_of(chip, struct exynos_irq_chip, chip);
@@ -52,10 +60,21 @@ static void exynos_irq_mask(struct irq_data *irqd)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset;
+ unsigned long reg_mask;
unsigned int mask;
unsigned long flags;
+ if (bank->eint_mask_offset)
+ reg_mask = bank->pctl_offset + bank->eint_mask_offset;
+ else
+ reg_mask = our_chip->eint_mask + bank->eint_offset;
+
+ if (clk_enable(bank->drvdata->pclk)) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock for masking IRQ\n");
+ return;
+ }
+
raw_spin_lock_irqsave(&bank->slock, flags);
mask = readl(bank->eint_base + reg_mask);
@@ -63,6 +82,8 @@ static void exynos_irq_mask(struct irq_data *irqd)
writel(mask, bank->eint_base + reg_mask);
raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ clk_disable(bank->drvdata->pclk);
}
static void exynos_irq_ack(struct irq_data *irqd)
@@ -70,9 +91,22 @@ static void exynos_irq_ack(struct irq_data *irqd)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- unsigned long reg_pend = our_chip->eint_pend + bank->eint_offset;
+ unsigned long reg_pend;
+
+ if (bank->eint_pend_offset)
+ reg_pend = bank->pctl_offset + bank->eint_pend_offset;
+ else
+ reg_pend = our_chip->eint_pend + bank->eint_offset;
+
+ if (clk_enable(bank->drvdata->pclk)) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock to ack IRQ\n");
+ return;
+ }
writel(1 << irqd->hwirq, bank->eint_base + reg_pend);
+
+ clk_disable(bank->drvdata->pclk);
}
static void exynos_irq_unmask(struct irq_data *irqd)
@@ -80,7 +114,7 @@ static void exynos_irq_unmask(struct irq_data *irqd)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset;
+ unsigned long reg_mask;
unsigned int mask;
unsigned long flags;
@@ -95,6 +129,17 @@ static void exynos_irq_unmask(struct irq_data *irqd)
if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK)
exynos_irq_ack(irqd);
+ if (bank->eint_mask_offset)
+ reg_mask = bank->pctl_offset + bank->eint_mask_offset;
+ else
+ reg_mask = our_chip->eint_mask + bank->eint_offset;
+
+ if (clk_enable(bank->drvdata->pclk)) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock for unmasking IRQ\n");
+ return;
+ }
+
raw_spin_lock_irqsave(&bank->slock, flags);
mask = readl(bank->eint_base + reg_mask);
@@ -102,6 +147,8 @@ static void exynos_irq_unmask(struct irq_data *irqd)
writel(mask, bank->eint_base + reg_mask);
raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ clk_disable(bank->drvdata->pclk);
}
static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
@@ -111,7 +158,8 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
unsigned int con, trig_type;
- unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
+ unsigned long reg_con;
+ int ret;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
@@ -139,14 +187,41 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
else
irq_set_handler_locked(irqd, handle_level_irq);
+ if (bank->eint_con_offset)
+ reg_con = bank->pctl_offset + bank->eint_con_offset;
+ else
+ reg_con = our_chip->eint_con + bank->eint_offset;
+
+ ret = clk_enable(bank->drvdata->pclk);
+ if (ret) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock for configuring IRQ type\n");
+ return ret;
+ }
+
con = readl(bank->eint_base + reg_con);
con &= ~(EXYNOS_EINT_CON_MASK << shift);
con |= trig_type << shift;
writel(con, bank->eint_base + reg_con);
+ clk_disable(bank->drvdata->pclk);
+
return 0;
}
+static int exynos_irq_set_affinity(struct irq_data *irqd,
+ const struct cpumask *dest, bool force)
+{
+ struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
+ struct samsung_pinctrl_drv_data *d = bank->drvdata;
+ struct irq_data *parent = irq_get_irq_data(d->irq);
+
+ if (parent)
+ return parent->chip->irq_set_affinity(parent, dest, force);
+
+ return -EINVAL;
+}
+
static int exynos_irq_request_resources(struct irq_data *irqd)
{
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
@@ -167,6 +242,14 @@ static int exynos_irq_request_resources(struct irq_data *irqd)
shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
+ ret = clk_enable(bank->drvdata->pclk);
+ if (ret) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock for configuring pin %s-%lu\n",
+ bank->name, irqd->hwirq);
+ return ret;
+ }
+
raw_spin_lock_irqsave(&bank->slock, flags);
con = readl(bank->pctl_base + reg_con);
@@ -176,6 +259,8 @@ static int exynos_irq_request_resources(struct irq_data *irqd)
raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->drvdata->pclk);
+
return 0;
}
@@ -190,6 +275,13 @@ static void exynos_irq_release_resources(struct irq_data *irqd)
shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
+ if (clk_enable(bank->drvdata->pclk)) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock for deconfiguring pin %s-%lu\n",
+ bank->name, irqd->hwirq);
+ return;
+ }
+
raw_spin_lock_irqsave(&bank->slock, flags);
con = readl(bank->pctl_base + reg_con);
@@ -199,6 +291,8 @@ static void exynos_irq_release_resources(struct irq_data *irqd)
raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->drvdata->pclk);
+
gpiochip_unlock_as_irq(&bank->gpio_chip, irqd->hwirq);
}
@@ -212,13 +306,14 @@ static const struct exynos_irq_chip exynos_gpio_irq_chip __initconst = {
.irq_mask = exynos_irq_mask,
.irq_ack = exynos_irq_ack,
.irq_set_type = exynos_irq_set_type,
+ .irq_set_affinity = exynos_irq_set_affinity,
.irq_request_resources = exynos_irq_request_resources,
.irq_release_resources = exynos_irq_release_resources,
},
.eint_con = EXYNOS_GPIO_ECON_OFFSET,
.eint_mask = EXYNOS_GPIO_EMASK_OFFSET,
.eint_pend = EXYNOS_GPIO_EPEND_OFFSET,
- /* eint_wake_mask_value not used */
+ /* eint_wake_mask_values not used */
};
static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq,
@@ -247,7 +342,19 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
unsigned int svc, group, pin;
int ret;
- svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET);
+ if (clk_enable(bank->drvdata->pclk)) {
+ dev_err(bank->gpio_chip.parent,
+ "unable to enable clock for handling IRQ\n");
+ return IRQ_NONE;
+ }
+
+ if (bank->eint_con_offset)
+ svc = readl(bank->eint_base + EXYNOSAUTO_SVC_OFFSET);
+ else
+ svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET);
+
+ clk_disable(bank->drvdata->pclk);
+
group = EXYNOS_SVC_GROUP(svc);
pin = svc & EXYNOS_SVC_NUM_MASK;
@@ -269,6 +376,37 @@ struct exynos_eint_gpio_save {
u32 eint_mask;
};
+static void exynos_eint_update_flt_reg(void __iomem *reg, int cnt, int con)
+{
+ unsigned int val, shift;
+ int i;
+
+ val = readl(reg);
+ for (i = 0; i < cnt; i++) {
+ shift = i * EXYNOS_FLTCON_LEN;
+ val &= ~(EXYNOS_FLTCON_DIGITAL << shift);
+ val |= con << shift;
+ }
+ writel(val, reg);
+}
+
+/*
+ * Set the desired filter (digital or analog delay) and enable it to
+ * every pin in the bank. Note the filter selection bitfield is only
+ * found on alive banks. The filter determines to what extent signal
+ * fluctuations received through the pad are considered glitches.
+ */
+static void exynos_eint_set_filter(struct samsung_pin_bank *bank, int filter)
+{
+ unsigned int off = EXYNOS_GPIO_EFLTCON_OFFSET + bank->eint_fltcon_offset;
+ void __iomem *reg = bank->drvdata->virt_base + off;
+ unsigned int con = EXYNOS_FLTCON_EN | filter;
+
+ for (int n = 0; n < bank->nr_pins; n += 4)
+ exynos_eint_update_flt_reg(reg + n,
+ min(bank->nr_pins - n, 4), con);
+}
+
/*
* exynos_eint_gpio_init() - setup handling of external gpio interrupts.
* @d: driver data of samsung pinctrl driver.
@@ -335,20 +473,65 @@ err_domains:
return ret;
}
+#define BITS_PER_U32 32
+static int gs101_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
+{
+ struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
+ struct samsung_pinctrl_drv_data *d = bank->drvdata;
+ u32 bit, wakeup_reg, shift;
+
+ bit = bank->eint_num + irqd->hwirq;
+ wakeup_reg = bit / BITS_PER_U32;
+ shift = bit - (wakeup_reg * BITS_PER_U32);
+
+ if (!on)
+ eint_wake_mask_values[wakeup_reg] |= BIT_U32(shift);
+ else
+ eint_wake_mask_values[wakeup_reg] &= ~BIT_U32(shift);
+
+ dev_info(d->dev, "wake %s for irq %d\n", str_enabled_disabled(on),
+ irqd->irq);
+
+ return 0;
+}
+
+static void
+gs101_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
+ struct exynos_irq_chip *irq_chip)
+{
+ struct regmap *pmu_regs;
+
+ if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) {
+ dev_warn(drvdata->dev,
+ "No PMU syscon available. Wake-up mask will not be set.\n");
+ return;
+ }
+
+ pmu_regs = drvdata->retention_ctrl->priv;
+
+ dev_dbg(drvdata->dev, "Setting external wakeup interrupt mask:\n");
+
+ for (int i = 0; i < irq_chip->eint_num_wakeup_reg; i++) {
+ dev_dbg(drvdata->dev, "\tWAKEUP_MASK%d[0x%X] value[0x%X]\n",
+ i, irq_chip->eint_wake_mask_reg + i * 4,
+ eint_wake_mask_values[i]);
+ regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg + i * 4,
+ eint_wake_mask_values[i]);
+ }
+}
+
static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
{
- struct irq_chip *chip = irq_data_get_irq_chip(irqd);
- struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
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 %u (%s-%lu)\n", on ? "enabled" : "disabled",
+ pr_info("wake %s for irq %u (%s-%lu)\n", str_enabled_disabled(on),
irqd->irq, bank->name, irqd->hwirq);
if (!on)
- *our_chip->eint_wake_mask_value |= bit;
+ eint_wake_mask_values[0] |= bit;
else
- *our_chip->eint_wake_mask_value &= ~bit;
+ eint_wake_mask_values[0] &= ~bit;
return 0;
}
@@ -368,10 +551,10 @@ exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
pmu_regs = drvdata->retention_ctrl->priv;
dev_info(drvdata->dev,
"Setting external wakeup interrupt mask: 0x%x\n",
- *irq_chip->eint_wake_mask_value);
+ eint_wake_mask_values[0]);
regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg,
- *irq_chip->eint_wake_mask_value);
+ eint_wake_mask_values[0]);
}
static void
@@ -390,11 +573,10 @@ s5pv210_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
clk_base = (void __iomem *) drvdata->retention_ctrl->priv;
- __raw_writel(*irq_chip->eint_wake_mask_value,
+ __raw_writel(eint_wake_mask_values[0],
clk_base + irq_chip->eint_wake_mask_reg);
}
-static u32 eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED;
/*
* irq_chip for wakeup interrupts
*/
@@ -412,7 +594,7 @@ static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = {
.eint_con = EXYNOS_WKUP_ECON_OFFSET,
.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
- .eint_wake_mask_value = &eint_wake_mask_value,
+ .eint_num_wakeup_reg = 1,
/* Only differences with exynos4210_wkup_irq_chip: */
.eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK,
.set_eint_wakeup_mask = s5pv210_pinctrl_set_eint_wakeup_mask,
@@ -432,7 +614,7 @@ static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
.eint_con = EXYNOS_WKUP_ECON_OFFSET,
.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
- .eint_wake_mask_value = &eint_wake_mask_value,
+ .eint_num_wakeup_reg = 1,
.eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK,
.set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask,
};
@@ -451,13 +633,50 @@ static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
.eint_con = EXYNOS7_WKUP_ECON_OFFSET,
.eint_mask = EXYNOS7_WKUP_EMASK_OFFSET,
.eint_pend = EXYNOS7_WKUP_EPEND_OFFSET,
- .eint_wake_mask_value = &eint_wake_mask_value,
+ .eint_num_wakeup_reg = 1,
+ .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
+ .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask,
+};
+
+static const struct exynos_irq_chip exynosautov920_wkup_irq_chip __initconst = {
+ .chip = {
+ .name = "exynosautov920_wkup_irq_chip",
+ .irq_unmask = exynos_irq_unmask,
+ .irq_mask = exynos_irq_mask,
+ .irq_ack = exynos_irq_ack,
+ .irq_set_type = exynos_irq_set_type,
+ .irq_set_wake = exynos_wkup_irq_set_wake,
+ .irq_request_resources = exynos_irq_request_resources,
+ .irq_release_resources = exynos_irq_release_resources,
+ },
+ .eint_num_wakeup_reg = 1,
.eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
.set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask,
};
+static const struct exynos_irq_chip gs101_wkup_irq_chip __initconst = {
+ .chip = {
+ .name = "gs101_wkup_irq_chip",
+ .irq_unmask = exynos_irq_unmask,
+ .irq_mask = exynos_irq_mask,
+ .irq_ack = exynos_irq_ack,
+ .irq_set_type = exynos_irq_set_type,
+ .irq_set_wake = gs101_wkup_irq_set_wake,
+ .irq_request_resources = exynos_irq_request_resources,
+ .irq_release_resources = exynos_irq_release_resources,
+ },
+ .eint_con = EXYNOS7_WKUP_ECON_OFFSET,
+ .eint_mask = EXYNOS7_WKUP_EMASK_OFFSET,
+ .eint_pend = EXYNOS7_WKUP_EPEND_OFFSET,
+ .eint_num_wakeup_reg = 3,
+ .eint_wake_mask_reg = GS101_EINT_WAKEUP_MASK,
+ .set_eint_wakeup_mask = gs101_pinctrl_set_eint_wakeup_mask,
+};
+
/* list of external wakeup controllers supported */
static const struct of_device_id exynos_wkup_irq_ids[] = {
+ { .compatible = "google,gs101-wakeup-eint",
+ .data = &gs101_wkup_irq_chip },
{ .compatible = "samsung,s5pv210-wakeup-eint",
.data = &s5pv210_wkup_irq_chip },
{ .compatible = "samsung,exynos4210-wakeup-eint",
@@ -468,6 +687,8 @@ static const struct of_device_id exynos_wkup_irq_ids[] = {
.data = &exynos7_wkup_irq_chip },
{ .compatible = "samsung,exynosautov9-wakeup-eint",
.data = &exynos7_wkup_irq_chip },
+ { .compatible = "samsung,exynosautov920-wakeup-eint",
+ .data = &exynosautov920_wkup_irq_chip },
{ }
};
@@ -508,6 +729,20 @@ static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
chained_irq_enter(chip, desc);
+ /*
+ * just enable the clock once here, to avoid an enable/disable dance for
+ * each bank.
+ */
+ if (eintd->nr_banks) {
+ struct samsung_pin_bank *b = eintd->banks[0];
+
+ if (clk_enable(b->drvdata->pclk)) {
+ dev_err(b->gpio_chip.parent,
+ "unable to enable clock for pending IRQs\n");
+ goto out;
+ }
+ }
+
for (i = 0; i < eintd->nr_banks; ++i) {
struct samsung_pin_bank *b = eintd->banks[i];
pend = readl(b->eint_base + b->irq_chip->eint_pend
@@ -517,9 +752,14 @@ static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
}
+ if (eintd->nr_banks)
+ clk_disable(eintd->banks[0]->drvdata->pclk);
+
+out:
chained_irq_exit(chip, desc);
}
+static int eint_num;
/*
* exynos_eint_wkup_init() - setup handling of external wakeup interrupts.
* @d: driver data of samsung pinctrl driver.
@@ -527,7 +767,7 @@ static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
__init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
{
struct device *dev = d->dev;
- struct device_node *wkup_np = NULL;
+ struct device_node *wkup_np __free(device_node) = NULL;
struct device_node *np;
struct samsung_pin_bank *bank;
struct exynos_weint_data *weint_data;
@@ -557,20 +797,20 @@ __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
bank->irq_chip = devm_kmemdup(dev, irq_chip, sizeof(*irq_chip),
GFP_KERNEL);
- if (!bank->irq_chip) {
- of_node_put(wkup_np);
+ if (!bank->irq_chip)
return -ENOMEM;
- }
bank->irq_chip->chip.name = bank->name;
bank->irq_domain = irq_domain_create_linear(bank->fwnode,
bank->nr_pins, &exynos_eint_irqd_ops, bank);
if (!bank->irq_domain) {
dev_err(dev, "wkup irq domain add failed\n");
- of_node_put(wkup_np);
return -ENXIO;
}
+ bank->eint_num = eint_num;
+ eint_num = eint_num + bank->nr_pins;
+
if (!fwnode_property_present(bank->fwnode, "interrupts")) {
bank->eint_type = EINT_TYPE_WKUP_MUX;
++muxed_banks;
@@ -580,10 +820,8 @@ __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
weint_data = devm_kcalloc(dev,
bank->nr_pins, sizeof(*weint_data),
GFP_KERNEL);
- if (!weint_data) {
- of_node_put(wkup_np);
+ if (!weint_data)
return -ENOMEM;
- }
for (idx = 0; idx < bank->nr_pins; ++idx) {
irq = irq_of_parse_and_map(to_of_node(bank->fwnode), idx);
@@ -600,13 +838,10 @@ __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
}
}
- if (!muxed_banks) {
- of_node_put(wkup_np);
+ if (!muxed_banks)
return 0;
- }
irq = irq_of_parse_and_map(wkup_np, 0);
- of_node_put(wkup_np);
if (!irq) {
dev_err(dev, "irq number for muxed EINTs not found\n");
return 0;
@@ -616,6 +851,7 @@ __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
+ muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL);
if (!muxed_data)
return -ENOMEM;
+ muxed_data->nr_banks = muxed_banks;
irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31,
muxed_data);
@@ -628,90 +864,194 @@ __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
muxed_data->banks[idx++] = bank;
}
- muxed_data->nr_banks = muxed_banks;
return 0;
}
-static void exynos_pinctrl_suspend_bank(
- struct samsung_pinctrl_drv_data *drvdata,
- struct samsung_pin_bank *bank)
+static void exynos_set_wakeup(struct samsung_pin_bank *bank)
+{
+ struct exynos_irq_chip *irq_chip;
+
+ if (bank->irq_chip) {
+ irq_chip = bank->irq_chip;
+ irq_chip->set_eint_wakeup_mask(bank->drvdata, irq_chip);
+ }
+}
+
+void exynos_pinctrl_suspend(struct samsung_pin_bank *bank)
{
struct exynos_eint_gpio_save *save = bank->soc_priv;
- void __iomem *regs = bank->eint_base;
+ const void __iomem *regs = bank->eint_base;
+
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ 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);
+ save->eint_mask = readl(regs + bank->irq_chip->eint_mask
+ + bank->eint_offset);
+
+ 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);
+ pr_debug("%s: save mask %#010x\n",
+ bank->name, save->eint_mask);
+ } else if (bank->eint_type == EINT_TYPE_WKUP) {
+ exynos_set_wakeup(bank);
+ }
+}
- 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);
- save->eint_mask = readl(regs + bank->irq_chip->eint_mask
- + bank->eint_offset);
+void gs101_pinctrl_suspend(struct samsung_pin_bank *bank)
+{
+ struct exynos_eint_gpio_save *save = bank->soc_priv;
+ const void __iomem *regs = bank->eint_base;
+
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
+ + bank->eint_offset);
+
+ save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ + bank->eint_fltcon_offset);
+
+ /* fltcon1 register only exists for pins 4-7 */
+ if (bank->nr_pins > 4)
+ save->eint_fltcon1 = readl(regs +
+ EXYNOS_GPIO_EFLTCON_OFFSET
+ + bank->eint_fltcon_offset + 4);
+
+ save->eint_mask = readl(regs + bank->irq_chip->eint_mask
+ + bank->eint_offset);
+
+ pr_debug("%s: save con %#010x\n",
+ bank->name, save->eint_con);
+ pr_debug("%s: save fltcon0 %#010x\n",
+ bank->name, save->eint_fltcon0);
+ if (bank->nr_pins > 4)
+ pr_debug("%s: save fltcon1 %#010x\n",
+ bank->name, save->eint_fltcon1);
+ pr_debug("%s: save mask %#010x\n",
+ bank->name, save->eint_mask);
+ } else if (bank->eint_type == EINT_TYPE_WKUP) {
+ exynos_set_wakeup(bank);
+ exynos_eint_set_filter(bank, EXYNOS_FLTCON_ANALOG);
+ }
+}
- 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);
- pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask);
+void exynosautov920_pinctrl_suspend(struct samsung_pin_bank *bank)
+{
+ struct exynos_eint_gpio_save *save = bank->soc_priv;
+ const void __iomem *regs = bank->eint_base;
+
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ save->eint_con = readl(regs + bank->pctl_offset +
+ bank->eint_con_offset);
+ save->eint_mask = readl(regs + bank->pctl_offset +
+ bank->eint_mask_offset);
+ pr_debug("%s: save con %#010x\n",
+ bank->name, save->eint_con);
+ pr_debug("%s: save mask %#010x\n",
+ bank->name, save->eint_mask);
+ } else if (bank->eint_type == EINT_TYPE_WKUP) {
+ exynos_set_wakeup(bank);
+ }
}
-void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
+void gs101_pinctrl_resume(struct samsung_pin_bank *bank)
{
- struct samsung_pin_bank *bank = drvdata->pin_banks;
- struct exynos_irq_chip *irq_chip = NULL;
- int i;
+ struct exynos_eint_gpio_save *save = bank->soc_priv;
- for (i = 0; i < drvdata->nr_banks; ++i, ++bank) {
- if (bank->eint_type == EINT_TYPE_GPIO)
- exynos_pinctrl_suspend_bank(drvdata, bank);
- else if (bank->eint_type == EINT_TYPE_WKUP) {
- if (!irq_chip) {
- irq_chip = bank->irq_chip;
- irq_chip->set_eint_wakeup_mask(drvdata,
- irq_chip);
- }
- }
+ void __iomem *regs = bank->eint_base;
+ void __iomem *eint_fltcfg0 = regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ + bank->eint_fltcon_offset;
+
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ 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(eint_fltcfg0), save->eint_fltcon0);
+
+ /* fltcon1 register only exists for pins 4-7 */
+ if (bank->nr_pins > 4)
+ pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name,
+ readl(eint_fltcfg0 + 4), save->eint_fltcon1);
+
+ pr_debug("%s: mask %#010x => %#010x\n", bank->name,
+ readl(regs + bank->irq_chip->eint_mask
+ + bank->eint_offset), save->eint_mask);
+
+ writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET
+ + bank->eint_offset);
+ writel(save->eint_fltcon0, eint_fltcfg0);
+
+ if (bank->nr_pins > 4)
+ writel(save->eint_fltcon1, eint_fltcfg0 + 4);
+ writel(save->eint_mask, regs + bank->irq_chip->eint_mask
+ + bank->eint_offset);
+ } else if (bank->eint_type == EINT_TYPE_WKUP) {
+ exynos_eint_set_filter(bank, EXYNOS_FLTCON_DIGITAL);
}
}
-static void exynos_pinctrl_resume_bank(
- struct samsung_pinctrl_drv_data *drvdata,
- struct samsung_pin_bank *bank)
+void exynos_pinctrl_resume(struct samsung_pin_bank *bank)
{
struct exynos_eint_gpio_save *save = bank->soc_priv;
void __iomem *regs = bank->eint_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);
- pr_debug("%s: mask %#010x => %#010x\n", bank->name,
- readl(regs + bank->irq_chip->eint_mask
- + bank->eint_offset), save->eint_mask);
-
- 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);
- writel(save->eint_mask, regs + bank->irq_chip->eint_mask
- + bank->eint_offset);
-}
-
-void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
-{
- struct samsung_pin_bank *bank = drvdata->pin_banks;
- int i;
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ 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);
+ pr_debug("%s: mask %#010x => %#010x\n", bank->name,
+ readl(regs + bank->irq_chip->eint_mask
+ + bank->eint_offset), save->eint_mask);
+
+ 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);
+ writel(save->eint_mask, regs + bank->irq_chip->eint_mask
+ + bank->eint_offset);
+ }
+}
- for (i = 0; i < drvdata->nr_banks; ++i, ++bank)
- if (bank->eint_type == EINT_TYPE_GPIO)
- exynos_pinctrl_resume_bank(drvdata, bank);
+void exynosautov920_pinctrl_resume(struct samsung_pin_bank *bank)
+{
+ struct exynos_eint_gpio_save *save = bank->soc_priv;
+ void __iomem *regs = bank->eint_base;
+
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ /* exynosautov920 has eint_con_offset for all but one bank */
+ if (!bank->eint_con_offset)
+ exynos_pinctrl_resume(bank);
+
+ pr_debug("%s: con %#010x => %#010x\n", bank->name,
+ readl(regs + bank->pctl_offset + bank->eint_con_offset),
+ save->eint_con);
+ pr_debug("%s: mask %#010x => %#010x\n", bank->name,
+ readl(regs + bank->pctl_offset +
+ bank->eint_mask_offset), save->eint_mask);
+
+ writel(save->eint_con,
+ regs + bank->pctl_offset + bank->eint_con_offset);
+ writel(save->eint_mask,
+ regs + bank->pctl_offset + bank->eint_mask_offset);
+ }
}
static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata)