pinctrl: samsung: support a bus clock
[linux-2.6-block.git] / drivers / pinctrl / samsung / pinctrl-samsung.c
index ed07e23e091220a925e7fd4646f3e4a651f999bf..f4607b8493fff473043eab62b90cf10f52729e20 100644 (file)
@@ -15,6 +15,7 @@
 // but provides extensions to which platform specific implementation of the gpio
 // and wakeup interrupts can be hooked to.
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/gpio/driver.h>
 #include <linux/init.h>
@@ -371,8 +372,8 @@ static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata,
 }
 
 /* enable or disable a pinmux function */
-static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
-                                       unsigned group)
+static int samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
+                               unsigned group)
 {
        struct samsung_pinctrl_drv_data *drvdata;
        const struct samsung_pin_bank_type *type;
@@ -382,6 +383,7 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
        unsigned long flags;
        const struct samsung_pmx_func *func;
        const struct samsung_pin_group *grp;
+       int ret;
 
        drvdata = pinctrl_dev_get_drvdata(pctldev);
        func = &drvdata->pmx_functions[selector];
@@ -397,6 +399,12 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
                reg += 4;
        }
 
+       ret = clk_enable(drvdata->pclk);
+       if (ret) {
+               dev_err(pctldev->dev, "failed to enable clock for setup\n");
+               return ret;
+       }
+
        raw_spin_lock_irqsave(&bank->slock, flags);
 
        data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
@@ -405,6 +413,10 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
        writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
 
        raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+       clk_disable(drvdata->pclk);
+
+       return 0;
 }
 
 /* enable a specified pinmux by writing to registers */
@@ -412,8 +424,7 @@ static int samsung_pinmux_set_mux(struct pinctrl_dev *pctldev,
                                  unsigned selector,
                                  unsigned group)
 {
-       samsung_pinmux_setup(pctldev, selector, group);
-       return 0;
+       return samsung_pinmux_setup(pctldev, selector, group);
 }
 
 /* list of pinmux callbacks for the pinmux vertical in pinctrl core */
@@ -436,6 +447,7 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
        u32 data, width, pin_offset, mask, shift;
        u32 cfg_value, cfg_reg;
        unsigned long flags;
+       int ret;
 
        drvdata = pinctrl_dev_get_drvdata(pctldev);
        pin_to_reg_bank(drvdata, pin, &reg_base, &pin_offset, &bank);
@@ -447,6 +459,12 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
        width = type->fld_width[cfg_type];
        cfg_reg = type->reg_offset[cfg_type];
 
+       ret = clk_enable(drvdata->pclk);
+       if (ret) {
+               dev_err(drvdata->dev, "failed to enable clock\n");
+               return ret;
+       }
+
        raw_spin_lock_irqsave(&bank->slock, flags);
 
        mask = (1 << width) - 1;
@@ -466,6 +484,8 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
 
        raw_spin_unlock_irqrestore(&bank->slock, flags);
 
+       clk_disable(drvdata->pclk);
+
        return 0;
 }
 
@@ -555,11 +575,19 @@ static void samsung_gpio_set_value(struct gpio_chip *gc,
 static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
 {
        struct samsung_pin_bank *bank = gpiochip_get_data(gc);
+       struct samsung_pinctrl_drv_data *drvdata = bank->drvdata;
        unsigned long flags;
 
+       if (clk_enable(drvdata->pclk)) {
+               dev_err(drvdata->dev, "failed to enable clock\n");
+               return;
+       }
+
        raw_spin_lock_irqsave(&bank->slock, flags);
        samsung_gpio_set_value(gc, offset, value);
        raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+       clk_disable(drvdata->pclk);
 }
 
 /* gpiolib gpio_get callback function */
@@ -569,12 +597,23 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
        u32 data;
        struct samsung_pin_bank *bank = gpiochip_get_data(gc);
        const struct samsung_pin_bank_type *type = bank->type;
+       struct samsung_pinctrl_drv_data *drvdata = bank->drvdata;
+       int ret;
 
        reg = bank->pctl_base + bank->pctl_offset;
 
+       ret = clk_enable(drvdata->pclk);
+       if (ret) {
+               dev_err(drvdata->dev, "failed to enable clock\n");
+               return ret;
+       }
+
        data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]);
        data >>= offset;
        data &= 1;
+
+       clk_disable(drvdata->pclk);
+
        return data;
 }
 
@@ -591,9 +630,11 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc,
        struct samsung_pin_bank *bank;
        void __iomem *reg;
        u32 data, mask, shift;
+       struct samsung_pinctrl_drv_data *drvdata;
 
        bank = gpiochip_get_data(gc);
        type = bank->type;
+       drvdata = bank->drvdata;
 
        reg = bank->pctl_base + bank->pctl_offset
                        + type->reg_offset[PINCFG_TYPE_FUNC];
@@ -619,12 +660,22 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc,
 static int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
 {
        struct samsung_pin_bank *bank = gpiochip_get_data(gc);
+       struct samsung_pinctrl_drv_data *drvdata = bank->drvdata;
        unsigned long flags;
        int ret;
 
+       ret = clk_enable(drvdata->pclk);
+       if (ret) {
+               dev_err(drvdata->dev, "failed to enable clock\n");
+               return ret;
+       }
+
        raw_spin_lock_irqsave(&bank->slock, flags);
        ret = samsung_gpio_set_direction(gc, offset, true);
        raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+       clk_disable(drvdata->pclk);
+
        return ret;
 }
 
@@ -633,14 +684,23 @@ static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
                                                        int value)
 {
        struct samsung_pin_bank *bank = gpiochip_get_data(gc);
+       struct samsung_pinctrl_drv_data *drvdata = bank->drvdata;
        unsigned long flags;
        int ret;
 
+       ret = clk_enable(drvdata->pclk);
+       if (ret) {
+               dev_err(drvdata->dev, "failed to enable clock\n");
+               return ret;
+       }
+
        raw_spin_lock_irqsave(&bank->slock, flags);
        samsung_gpio_set_value(gc, offset, value);
        ret = samsung_gpio_set_direction(gc, offset, false);
        raw_spin_unlock_irqrestore(&bank->slock, flags);
 
+       clk_disable(drvdata->pclk);
+
        return ret;
 }
 
@@ -1164,6 +1224,12 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
                }
        }
 
+       drvdata->pclk = devm_clk_get_optional_prepared(dev, "pclk");
+       if (IS_ERR(drvdata->pclk)) {
+               ret = PTR_ERR(drvdata->pclk);
+               goto err_put_banks;
+       }
+
        ret = samsung_pinctrl_register(pdev, drvdata);
        if (ret)
                goto err_put_banks;
@@ -1202,6 +1268,13 @@ static int __maybe_unused samsung_pinctrl_suspend(struct device *dev)
        struct samsung_pinctrl_drv_data *drvdata = dev_get_drvdata(dev);
        int i;
 
+       i = clk_enable(drvdata->pclk);
+       if (i) {
+               dev_err(drvdata->dev,
+                       "failed to enable clock for saving state\n");
+               return i;
+       }
+
        for (i = 0; i < drvdata->nr_banks; i++) {
                struct samsung_pin_bank *bank = &drvdata->pin_banks[i];
                const void __iomem *reg = bank->pctl_base + bank->pctl_offset;
@@ -1231,6 +1304,8 @@ static int __maybe_unused samsung_pinctrl_suspend(struct device *dev)
                }
        }
 
+       clk_disable(drvdata->pclk);
+
        if (drvdata->suspend)
                drvdata->suspend(drvdata);
        if (drvdata->retention_ctrl && drvdata->retention_ctrl->enable)
@@ -1250,8 +1325,20 @@ static int __maybe_unused samsung_pinctrl_suspend(struct device *dev)
 static int __maybe_unused samsung_pinctrl_resume(struct device *dev)
 {
        struct samsung_pinctrl_drv_data *drvdata = dev_get_drvdata(dev);
+       int ret;
        int i;
 
+       /*
+        * enable clock before the callback, as we don't want to have to deal
+        * with callback cleanup on clock failures.
+        */
+       ret = clk_enable(drvdata->pclk);
+       if (ret) {
+               dev_err(drvdata->dev,
+                       "failed to enable clock for restoring state\n");
+               return ret;
+       }
+
        if (drvdata->resume)
                drvdata->resume(drvdata);
 
@@ -1286,6 +1373,8 @@ static int __maybe_unused samsung_pinctrl_resume(struct device *dev)
                                writel(bank->pm_save[type], reg + offs[type]);
        }
 
+       clk_disable(drvdata->pclk);
+
        if (drvdata->retention_ctrl && drvdata->retention_ctrl->disable)
                drvdata->retention_ctrl->disable(drvdata);