summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/pinctrl-exynos.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinctrl-exynos.c')
-rw-r--r--drivers/pinctrl/pinctrl-exynos.c204
1 files changed, 182 insertions, 22 deletions
diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c
index 2d76f66a2e0b..a74b3cbd7451 100644
--- a/drivers/pinctrl/pinctrl-exynos.c
+++ b/drivers/pinctrl/pinctrl-exynos.c
@@ -50,37 +50,58 @@ static const struct of_device_id exynos_wkup_irq_ids[] = {
{ }
};
-static void exynos_gpio_irq_unmask(struct irq_data *irqd)
+static void exynos_gpio_irq_mask(struct irq_data *irqd)
{
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset;
unsigned long mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->slock, flags);
mask = readl(d->virt_base + reg_mask);
- mask &= ~(1 << irqd->hwirq);
+ mask |= 1 << irqd->hwirq;
writel(mask, d->virt_base + reg_mask);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
}
-static void exynos_gpio_irq_mask(struct irq_data *irqd)
+static void exynos_gpio_irq_ack(struct irq_data *irqd)
{
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
struct samsung_pinctrl_drv_data *d = bank->drvdata;
- unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset;
- unsigned long mask;
+ unsigned long reg_pend = d->ctrl->geint_pend + bank->eint_offset;
- mask = readl(d->virt_base + reg_mask);
- mask |= 1 << irqd->hwirq;
- writel(mask, d->virt_base + reg_mask);
+ writel(1 << irqd->hwirq, d->virt_base + reg_pend);
}
-static void exynos_gpio_irq_ack(struct irq_data *irqd)
+static void exynos_gpio_irq_unmask(struct irq_data *irqd)
{
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
struct samsung_pinctrl_drv_data *d = bank->drvdata;
- unsigned long reg_pend = d->ctrl->geint_pend + bank->eint_offset;
+ unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset;
+ unsigned long mask;
+ unsigned long flags;
- writel(1 << irqd->hwirq, d->virt_base + reg_pend);
+ /*
+ * Ack level interrupts right before unmask
+ *
+ * If we don't do this we'll get a double-interrupt. Level triggered
+ * interrupts must not fire an interrupt if the level is not
+ * _currently_ active, even if it was active while the interrupt was
+ * masked.
+ */
+ if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK)
+ exynos_gpio_irq_ack(irqd);
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ mask = readl(d->virt_base + reg_mask);
+ mask &= ~(1 << irqd->hwirq);
+ writel(mask, d->virt_base + reg_mask);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
}
static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
@@ -258,37 +279,58 @@ err_domains:
return ret;
}
-static void exynos_wkup_irq_unmask(struct irq_data *irqd)
+static void exynos_wkup_irq_mask(struct irq_data *irqd)
{
struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd);
struct samsung_pinctrl_drv_data *d = b->drvdata;
unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset;
unsigned long mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&b->slock, flags);
mask = readl(d->virt_base + reg_mask);
- mask &= ~(1 << irqd->hwirq);
+ mask |= 1 << irqd->hwirq;
writel(mask, d->virt_base + reg_mask);
+
+ spin_unlock_irqrestore(&b->slock, flags);
}
-static void exynos_wkup_irq_mask(struct irq_data *irqd)
+static void exynos_wkup_irq_ack(struct irq_data *irqd)
{
struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd);
struct samsung_pinctrl_drv_data *d = b->drvdata;
- unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset;
- unsigned long mask;
+ unsigned long pend = d->ctrl->weint_pend + b->eint_offset;
- mask = readl(d->virt_base + reg_mask);
- mask |= 1 << irqd->hwirq;
- writel(mask, d->virt_base + reg_mask);
+ writel(1 << irqd->hwirq, d->virt_base + pend);
}
-static void exynos_wkup_irq_ack(struct irq_data *irqd)
+static void exynos_wkup_irq_unmask(struct irq_data *irqd)
{
struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd);
struct samsung_pinctrl_drv_data *d = b->drvdata;
- unsigned long pend = d->ctrl->weint_pend + b->eint_offset;
+ unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset;
+ unsigned long mask;
+ unsigned long flags;
- writel(1 << irqd->hwirq, d->virt_base + pend);
+ /*
+ * Ack level interrupts right before unmask
+ *
+ * If we don't do this we'll get a double-interrupt. Level triggered
+ * interrupts must not fire an interrupt if the level is not
+ * _currently_ active, even if it was active while the interrupt was
+ * masked.
+ */
+ if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK)
+ exynos_wkup_irq_ack(irqd);
+
+ spin_lock_irqsave(&b->slock, flags);
+
+ mask = readl(d->virt_base + reg_mask);
+ mask &= ~(1 << irqd->hwirq);
+ writel(mask, d->virt_base + reg_mask);
+
+ spin_unlock_irqrestore(&b->slock, flags);
}
static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type)
@@ -941,3 +983,121 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
.label = "exynos5250-gpio-ctrl3",
},
};
+
+/* pin banks of exynos5420 pin-controller 0 */
+static struct samsung_pin_bank exynos5420_pin_banks0[] = {
+ EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpy7", 0x00),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c),
+};
+
+/* pin banks of exynos5420 pin-controller 1 */
+static struct samsung_pin_bank exynos5420_pin_banks1[] = {
+ EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpc0", 0x00),
+ EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpc1", 0x04),
+ EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpc2", 0x08),
+ EXYNOS_PIN_BANK_EINTG(4, 0x060, "gpc3", 0x0c),
+ EXYNOS_PIN_BANK_EINTG(2, 0x080, "gpc4", 0x10),
+ EXYNOS_PIN_BANK_EINTG(8, 0x0A0, "gpd1", 0x14),
+ EXYNOS_PIN_BANK_EINTN(6, 0x0C0, "gpy0"),
+ EXYNOS_PIN_BANK_EINTN(4, 0x0E0, "gpy1"),
+ EXYNOS_PIN_BANK_EINTN(6, 0x100, "gpy2"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x120, "gpy3"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x140, "gpy4"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x160, "gpy5"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x180, "gpy6"),
+};
+
+/* pin banks of exynos5420 pin-controller 2 */
+static struct samsung_pin_bank exynos5420_pin_banks2[] = {
+ EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpe0", 0x00),
+ EXYNOS_PIN_BANK_EINTG(2, 0x020, "gpe1", 0x04),
+ EXYNOS_PIN_BANK_EINTG(6, 0x040, "gpf0", 0x08),
+ EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpf1", 0x0c),
+ EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpg0", 0x10),
+ EXYNOS_PIN_BANK_EINTG(8, 0x0A0, "gpg1", 0x14),
+ EXYNOS_PIN_BANK_EINTG(2, 0x0C0, "gpg2", 0x18),
+ EXYNOS_PIN_BANK_EINTG(4, 0x0E0, "gpj4", 0x1c),
+};
+
+/* pin banks of exynos5420 pin-controller 3 */
+static struct samsung_pin_bank exynos5420_pin_banks3[] = {
+ EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00),
+ EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04),
+ EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpa2", 0x08),
+ EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpb0", 0x0c),
+ EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpb1", 0x10),
+ EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpb2", 0x14),
+ EXYNOS_PIN_BANK_EINTG(8, 0x0C0, "gpb3", 0x18),
+ EXYNOS_PIN_BANK_EINTG(2, 0x0E0, "gpb4", 0x1c),
+ EXYNOS_PIN_BANK_EINTG(8, 0x100, "gph0", 0x20),
+};
+
+/* pin banks of exynos5420 pin-controller 4 */
+static struct samsung_pin_bank exynos5420_pin_banks4[] = {
+ EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz", 0x00),
+};
+
+/*
+ * Samsung pinctrl driver data for Exynos5420 SoC. Exynos5420 SoC includes
+ * four gpio/pin-mux/pinconfig controllers.
+ */
+struct samsung_pin_ctrl exynos5420_pin_ctrl[] = {
+ {
+ /* pin-controller instance 0 data */
+ .pin_banks = exynos5420_pin_banks0,
+ .nr_banks = ARRAY_SIZE(exynos5420_pin_banks0),
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .weint_con = EXYNOS_WKUP_ECON_OFFSET,
+ .weint_mask = EXYNOS_WKUP_EMASK_OFFSET,
+ .weint_pend = EXYNOS_WKUP_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .eint_wkup_init = exynos_eint_wkup_init,
+ .label = "exynos5420-gpio-ctrl0",
+ }, {
+ /* pin-controller instance 1 data */
+ .pin_banks = exynos5420_pin_banks1,
+ .nr_banks = ARRAY_SIZE(exynos5420_pin_banks1),
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .label = "exynos5420-gpio-ctrl1",
+ }, {
+ /* pin-controller instance 2 data */
+ .pin_banks = exynos5420_pin_banks2,
+ .nr_banks = ARRAY_SIZE(exynos5420_pin_banks2),
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .label = "exynos5420-gpio-ctrl2",
+ }, {
+ /* pin-controller instance 3 data */
+ .pin_banks = exynos5420_pin_banks3,
+ .nr_banks = ARRAY_SIZE(exynos5420_pin_banks3),
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .label = "exynos5420-gpio-ctrl3",
+ }, {
+ /* pin-controller instance 4 data */
+ .pin_banks = exynos5420_pin_banks4,
+ .nr_banks = ARRAY_SIZE(exynos5420_pin_banks4),
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .label = "exynos5420-gpio-ctrl4",
+ },
+};