aboutsummaryrefslogtreecommitdiff
path: root/drivers/pinctrl/samsung/pinctrl-samsung.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/samsung/pinctrl-samsung.c')
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.c95
1 files changed, 91 insertions, 4 deletions
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index ed07e23e0912..623df65a5d6f 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -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;
}
@@ -619,12 +658,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 +682,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 +1222,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 +1266,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 +1302,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 +1323,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 +1371,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);