Merge tag 'samsung-pinctrl-6.10' of https://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / pinctrl / samsung / pinctrl-exynos.c
index 871c1eb46ddfd0417a0161912ef2bdc58abc1969..ce5e6783b5b99f4a90a8bfe0e25c2e4481a2afce 100644 (file)
@@ -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>
@@ -61,6 +62,12 @@ static void exynos_irq_mask(struct irq_data *irqd)
        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);
@@ -68,6 +75,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)
@@ -82,7 +91,15 @@ static void exynos_irq_ack(struct irq_data *irqd)
        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)
@@ -110,6 +127,12 @@ static void exynos_irq_unmask(struct irq_data *irqd)
        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);
@@ -117,6 +140,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)
@@ -127,6 +152,7 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
        unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
        unsigned int con, trig_type;
        unsigned long reg_con;
+       int ret;
 
        switch (type) {
        case IRQ_TYPE_EDGE_RISING:
@@ -159,11 +185,20 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
        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;
 }
 
@@ -200,6 +235,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);
@@ -209,6 +252,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;
 }
 
@@ -223,6 +268,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);
@@ -232,6 +284,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);
 }
 
@@ -281,10 +335,19 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
        unsigned int svc, group, pin;
        int ret;
 
+       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;
 
@@ -563,6 +626,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");
+                       return;
+               }
+       }
+
        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
@@ -572,6 +649,9 @@ 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);
+
        chained_irq_exit(chip, desc);
 }
 
@@ -695,6 +775,12 @@ static void exynos_pinctrl_suspend_bank(
        struct exynos_eint_gpio_save *save = bank->soc_priv;
        const void __iomem *regs = bank->eint_base;
 
+       if (clk_enable(bank->drvdata->pclk)) {
+               dev_err(bank->gpio_chip.parent,
+                       "unable to enable clock for saving state\n");
+               return;
+       }
+
        save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
                                                + bank->eint_offset);
        save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
@@ -704,6 +790,8 @@ static void exynos_pinctrl_suspend_bank(
        save->eint_mask = readl(regs + bank->irq_chip->eint_mask
                                                + bank->eint_offset);
 
+       clk_disable(bank->drvdata->pclk);
+
        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);
@@ -716,9 +804,17 @@ static void exynosauto_pinctrl_suspend_bank(struct samsung_pinctrl_drv_data *drv
        struct exynos_eint_gpio_save *save = bank->soc_priv;
        const void __iomem *regs = bank->eint_base;
 
+       if (clk_enable(bank->drvdata->pclk)) {
+               dev_err(bank->gpio_chip.parent,
+                       "unable to enable clock for saving state\n");
+               return;
+       }
+
        save->eint_con = readl(regs + bank->pctl_offset + bank->eint_con_offset);
        save->eint_mask = readl(regs + bank->pctl_offset + bank->eint_mask_offset);
 
+       clk_disable(bank->drvdata->pclk);
+
        pr_debug("%s: save     con %#010x\n", bank->name, save->eint_con);
        pr_debug("%s: save    mask %#010x\n", bank->name, save->eint_mask);
 }
@@ -753,6 +849,12 @@ static void exynos_pinctrl_resume_bank(
        struct exynos_eint_gpio_save *save = bank->soc_priv;
        void __iomem *regs = bank->eint_base;
 
+       if (clk_enable(bank->drvdata->pclk)) {
+               dev_err(bank->gpio_chip.parent,
+                       "unable to enable clock for restoring state\n");
+               return;
+       }
+
        pr_debug("%s:     con %#010x => %#010x\n", bank->name,
                        readl(regs + EXYNOS_GPIO_ECON_OFFSET
                        + bank->eint_offset), save->eint_con);
@@ -774,6 +876,8 @@ static void exynos_pinctrl_resume_bank(
                                                + 2 * bank->eint_offset + 4);
        writel(save->eint_mask, regs + bank->irq_chip->eint_mask
                                                + bank->eint_offset);
+
+       clk_disable(bank->drvdata->pclk);
 }
 
 static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvdata,
@@ -782,6 +886,12 @@ static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvd
        struct exynos_eint_gpio_save *save = bank->soc_priv;
        void __iomem *regs = bank->eint_base;
 
+       if (clk_enable(bank->drvdata->pclk)) {
+               dev_err(bank->gpio_chip.parent,
+                       "unable to enable clock for restoring state\n");
+               return;
+       }
+
        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,
@@ -789,6 +899,8 @@ static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvd
 
        writel(save->eint_con, regs + bank->pctl_offset + bank->eint_con_offset);
        writel(save->eint_mask, regs + bank->pctl_offset + bank->eint_mask_offset);
+
+       clk_disable(bank->drvdata->pclk);
 }
 
 void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)