pinctrl: samsung: Add support for pull-up and pull-down
authorVishnu Reddy <vishnu.reddy@samsung.com>
Mon, 29 Jul 2024 15:36:31 +0000 (21:06 +0530)
committerKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Thu, 8 Aug 2024 15:22:04 +0000 (17:22 +0200)
Gpiolib framework has the implementation of setting up the
PUD configuration for GPIO pins but there is no driver support.

Add support to handle the PUD configuration request from the
userspace in samsung pinctrl driver.

Signed-off-by: Vishnu Reddy <vishnu.reddy@samsung.com>
Link: https://lore.kernel.org/r/20240729153631.24536-1-vishnu.reddy@samsung.com
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
drivers/pinctrl/samsung/pinctrl-exynos-arm.c
drivers/pinctrl/samsung/pinctrl-s3c64xx.c
drivers/pinctrl/samsung/pinctrl-samsung.c
drivers/pinctrl/samsung/pinctrl-samsung.h

index 85ddf49a5188594081b5adfcd0c890c570ac1fab..d3d8672f74dc62c45ee563a74587c76e28fb2156 100644 (file)
@@ -40,6 +40,19 @@ static const struct samsung_pin_bank_type bank_type_alive = {
 #define S5P_OTHERS_RET_MMC             (1 << 29)
 #define S5P_OTHERS_RET_UART            (1 << 28)
 
+#define S5P_PIN_PULL_DISABLE           0
+#define S5P_PIN_PULL_DOWN              1
+#define S5P_PIN_PULL_UP                        2
+
+static void s5pv210_pud_value_init(struct samsung_pinctrl_drv_data *drvdata)
+{
+       unsigned int  *pud_val = drvdata->pud_val;
+
+       pud_val[PUD_PULL_DISABLE] = S5P_PIN_PULL_DISABLE;
+       pud_val[PUD_PULL_DOWN] = S5P_PIN_PULL_DOWN;
+       pud_val[PUD_PULL_UP] = S5P_PIN_PULL_UP;
+}
+
 static void s5pv210_retention_disable(struct samsung_pinctrl_drv_data *drvdata)
 {
        void __iomem *clk_base = (void __iomem *)drvdata->retention_ctrl->priv;
@@ -133,6 +146,7 @@ static const struct samsung_pin_ctrl s5pv210_pin_ctrl[] __initconst = {
                .nr_banks       = ARRAY_SIZE(s5pv210_pin_bank),
                .eint_gpio_init = exynos_eint_gpio_init,
                .eint_wkup_init = exynos_eint_wkup_init,
+               .pud_value_init = s5pv210_pud_value_init,
                .suspend        = exynos_pinctrl_suspend,
                .resume         = exynos_pinctrl_resume,
                .retention_data = &s5pv210_retention_data,
index c5d92db4fdb1d3f0643d22881bd84b0fa3d511a5..68715c09baa9d0380dd45d50e485c1a1f2422fa9 100644 (file)
 #define EINT_CON_MASK          0xF
 #define EINT_CON_LEN           4
 
+#define S3C_PIN_PULL_DISABLE   0
+#define S3C_PIN_PULL_DOWN      1
+#define S3C_PIN_PULL_UP                2
+
 static const struct samsung_pin_bank_type bank_type_4bit_off = {
        .fld_width = { 4, 1, 2, 0, 2, 2, },
        .reg_offset = { 0x00, 0x04, 0x08, 0, 0x0c, 0x10, },
@@ -255,6 +259,15 @@ static int s3c64xx_irq_get_trigger(unsigned int type)
        return trigger;
 }
 
+static void s3c64xx_pud_value_init(struct samsung_pinctrl_drv_data *drvdata)
+{
+       unsigned int  *pud_val = drvdata->pud_val;
+
+       pud_val[PUD_PULL_DISABLE] = S3C_PIN_PULL_DISABLE;
+       pud_val[PUD_PULL_DOWN] = S3C_PIN_PULL_DOWN;
+       pud_val[PUD_PULL_UP] = S3C_PIN_PULL_UP;
+}
+
 static void s3c64xx_irq_set_handler(struct irq_data *d, unsigned int type)
 {
        /* Edge- and level-triggered interrupts need different handlers */
@@ -797,6 +810,7 @@ static const struct samsung_pin_ctrl s3c64xx_pin_ctrl[] __initconst = {
                .nr_banks       = ARRAY_SIZE(s3c64xx_pin_banks0),
                .eint_gpio_init = s3c64xx_eint_gpio_init,
                .eint_wkup_init = s3c64xx_eint_eint0_init,
+               .pud_value_init = s3c64xx_pud_value_init,
        },
 };
 
index 0651d8324ff2358f6461a5d625b34838701366bf..e4464ee815f935178ad65dcf59295142e1da38ec 100644 (file)
@@ -988,6 +988,77 @@ static int samsung_pinctrl_unregister(struct platform_device *pdev,
        return 0;
 }
 
+static void samsung_pud_value_init(struct samsung_pinctrl_drv_data *drvdata)
+{
+       unsigned int  *pud_val = drvdata->pud_val;
+
+       pud_val[PUD_PULL_DISABLE] = EXYNOS_PIN_PUD_PULL_DISABLE;
+       pud_val[PUD_PULL_DOWN] = EXYNOS_PIN_PID_PULL_DOWN;
+       pud_val[PUD_PULL_UP] = EXYNOS_PIN_PID_PULL_UP;
+}
+
+/*
+ * Enable or Disable the pull-down and pull-up for the gpio pins in the
+ * PUD register.
+ */
+static void samsung_gpio_set_pud(struct gpio_chip *gc, unsigned int offset,
+                                unsigned int value)
+{
+       struct samsung_pin_bank *bank = gpiochip_get_data(gc);
+       const struct samsung_pin_bank_type *type = bank->type;
+       void __iomem *reg;
+       unsigned int data, mask;
+
+       reg = bank->pctl_base + bank->pctl_offset;
+       data = readl(reg + type->reg_offset[PINCFG_TYPE_PUD]);
+       mask = (1 << type->fld_width[PINCFG_TYPE_PUD]) - 1;
+       data &= ~(mask << (offset * type->fld_width[PINCFG_TYPE_PUD]));
+       data |= value << (offset * type->fld_width[PINCFG_TYPE_PUD]);
+       writel(data, reg + type->reg_offset[PINCFG_TYPE_PUD]);
+}
+
+/*
+ * Identify the type of PUD config based on the gpiolib request to enable
+ * or disable the PUD config.
+ */
+static int samsung_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
+                                  unsigned long config)
+{
+       struct samsung_pin_bank *bank = gpiochip_get_data(gc);
+       struct samsung_pinctrl_drv_data *drvdata = bank->drvdata;
+       unsigned int value;
+       int ret = 0;
+       unsigned long flags;
+
+       switch (pinconf_to_config_param(config)) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               value = drvdata->pud_val[PUD_PULL_DISABLE];
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               value = drvdata->pud_val[PUD_PULL_DOWN];
+               break;
+       case PIN_CONFIG_BIAS_PULL_UP:
+               value = drvdata->pud_val[PUD_PULL_UP];
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       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_pud(gc, offset, value);
+       raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+       clk_disable(drvdata->pclk);
+
+       return ret;
+}
+
 static const struct gpio_chip samsung_gpiolib_chip = {
        .request = gpiochip_generic_request,
        .free = gpiochip_generic_free,
@@ -997,6 +1068,7 @@ static const struct gpio_chip samsung_gpiolib_chip = {
        .direction_output = samsung_gpio_direction_output,
        .to_irq = samsung_gpio_to_irq,
        .add_pin_ranges = samsung_add_pin_ranges,
+       .set_config = samsung_gpio_set_config,
        .owner = THIS_MODULE,
 };
 
@@ -1228,6 +1300,11 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
        if (ctrl->eint_wkup_init)
                ctrl->eint_wkup_init(drvdata);
 
+       if (ctrl->pud_value_init)
+               ctrl->pud_value_init(drvdata);
+       else
+               samsung_pud_value_init(drvdata);
+
        ret = samsung_gpiolib_register(pdev, drvdata);
        if (ret)
                goto err_unregister;
index d50ba6f07d5df3d19439b62c41f522821076278d..a1e7377bd890b7e058cd2c38a5b924e4e4384163 100644 (file)
@@ -61,6 +61,25 @@ enum pincfg_type {
 #define PIN_CON_FUNC_INPUT             0x0
 #define PIN_CON_FUNC_OUTPUT            0x1
 
+/* Values for the pin PUD register */
+#define EXYNOS_PIN_PUD_PULL_DISABLE    0x0
+#define EXYNOS_PIN_PID_PULL_DOWN       0x1
+#define EXYNOS_PIN_PID_PULL_UP         0x3
+
+/*
+ * enum pud_index - Possible index values to access the pud_val array.
+ * @PUD_PULL_DISABLE: Index for the value of pud disable
+ * @PUD_PULL_DOWN: Index for the value of pull down enable
+ * @PUD_PULL_UP: Index for the value of pull up enable
+ * @PUD_MAX: Maximum value of the index
+ */
+enum pud_index {
+       PUD_PULL_DISABLE,
+       PUD_PULL_DOWN,
+       PUD_PULL_UP,
+       PUD_MAX,
+};
+
 /**
  * enum eint_type - possible external interrupt types.
  * @EINT_TYPE_NONE: bank does not support external interrupts
@@ -261,6 +280,7 @@ struct samsung_pin_ctrl {
 
        int             (*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
        int             (*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
+       void            (*pud_value_init)(struct samsung_pinctrl_drv_data *drvdata);
        void            (*suspend)(struct samsung_pinctrl_drv_data *);
        void            (*resume)(struct samsung_pinctrl_drv_data *);
 };
@@ -307,6 +327,7 @@ struct samsung_pinctrl_drv_data {
        struct samsung_pin_bank         *pin_banks;
        unsigned int                    nr_banks;
        unsigned int                    nr_pins;
+       unsigned int                    pud_val[PUD_MAX];
 
        struct samsung_retention_ctrl   *retention_ctrl;