pinctrl: sh-pfc: Add drive strength support
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Wed, 23 Mar 2016 14:06:00 +0000 (16:06 +0200)
committerGeert Uytterhoeven <geert+renesas@glider.be>
Tue, 29 Mar 2016 07:23:01 +0000 (09:23 +0200)
Add support for the drive-strengh pin configuration using the generic
pinconf DT bindings.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt
drivers/pinctrl/sh-pfc/core.c
drivers/pinctrl/sh-pfc/core.h
drivers/pinctrl/sh-pfc/pinctrl.c
drivers/pinctrl/sh-pfc/sh_pfc.h

index ffadb7a371f6e94ee1422e04cf2598c3b5d5ec31..74e6ec0339d6fb6e3f37d158def03df27f53b863 100644 (file)
@@ -72,8 +72,8 @@ Pin Configuration Node Properties:
 
 The pin configuration parameters use the generic pinconf bindings defined in
 pinctrl-bindings.txt in this directory. The supported parameters are
-bias-disable, bias-pull-up, bias-pull-down and power-source. For pins that
-have a configurable I/O voltage, the power-source value should be the
+bias-disable, bias-pull-up, bias-pull-down, drive strength and power-source. For
+pins that have a configurable I/O voltage, the power-source value should be the
 nominal I/O voltage in millivolts.
 
 
index dc3609f0c60b6b50d10c76bba38b4f73952d5c1e..0497bbb8a8e791ea1a78ded7fcf5a0767daeee1a 100644 (file)
@@ -175,6 +175,21 @@ void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width,
        BUG();
 }
 
+u32 sh_pfc_read_reg(struct sh_pfc *pfc, u32 reg, unsigned int width)
+{
+       return sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, reg), width);
+}
+
+void sh_pfc_write_reg(struct sh_pfc *pfc, u32 reg, unsigned int width, u32 data)
+{
+       if (pfc->info->unlock_reg)
+               sh_pfc_write_raw_reg(
+                       sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32,
+                       ~data);
+
+       sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, reg), width, data);
+}
+
 static void sh_pfc_config_reg_helper(struct sh_pfc *pfc,
                                     const struct pinmux_cfg_reg *crp,
                                     unsigned int in_pos,
index 62f53b22ae85004628672533a446f6fade5a1a32..fc05d0c516f33e5e286d04e762a3edb84cf41aa4 100644 (file)
@@ -62,6 +62,9 @@ int sh_pfc_unregister_pinctrl(struct sh_pfc *pfc);
 u32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width);
 void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width,
                          u32 data);
+u32 sh_pfc_read_reg(struct sh_pfc *pfc, u32 reg, unsigned int width);
+void sh_pfc_write_reg(struct sh_pfc *pfc, u32 reg, unsigned int width,
+                     u32 data);
 
 int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin);
 int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type);
index 87b0a599afafb62498142bda6f8ae3057e3ce03f..8efaa0631be618a919079f3aa01edc94a1125407 100644 (file)
@@ -476,6 +476,91 @@ static const struct pinmux_ops sh_pfc_pinmux_ops = {
        .gpio_set_direction     = sh_pfc_gpio_set_direction,
 };
 
+static u32 sh_pfc_pinconf_find_drive_strength_reg(struct sh_pfc *pfc,
+               unsigned int pin, unsigned int *offset, unsigned int *size)
+{
+       const struct pinmux_drive_reg_field *field;
+       const struct pinmux_drive_reg *reg;
+       unsigned int i;
+
+       for (reg = pfc->info->drive_regs; reg->reg; ++reg) {
+               for (i = 0; i < ARRAY_SIZE(reg->fields); ++i) {
+                       field = &reg->fields[i];
+
+                       if (field->size && field->pin == pin) {
+                               *offset = field->offset;
+                               *size = field->size;
+
+                               return reg->reg;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int sh_pfc_pinconf_get_drive_strength(struct sh_pfc *pfc,
+                                            unsigned int pin)
+{
+       unsigned long flags;
+       unsigned int offset;
+       unsigned int size;
+       u32 reg;
+       u32 val;
+
+       reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size);
+       if (!reg)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pfc->lock, flags);
+       val = sh_pfc_read_reg(pfc, reg, 32);
+       spin_unlock_irqrestore(&pfc->lock, flags);
+
+       val = (val >> offset) & GENMASK(size - 1, 0);
+
+       /* Convert the value to mA based on a full drive strength value of 24mA.
+        * We can make the full value configurable later if needed.
+        */
+       return (val + 1) * (size == 2 ? 6 : 3);
+}
+
+static int sh_pfc_pinconf_set_drive_strength(struct sh_pfc *pfc,
+                                            unsigned int pin, u16 strength)
+{
+       unsigned long flags;
+       unsigned int offset;
+       unsigned int size;
+       unsigned int step;
+       u32 reg;
+       u32 val;
+
+       reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size);
+       if (!reg)
+               return -EINVAL;
+
+       step = size == 2 ? 6 : 3;
+
+       if (strength < step || strength > 24)
+               return -EINVAL;
+
+       /* Convert the value from mA based on a full drive strength value of
+        * 24mA. We can make the full value configurable later if needed.
+        */
+       strength = strength / step - 1;
+
+       spin_lock_irqsave(&pfc->lock, flags);
+
+       val = sh_pfc_read_reg(pfc, reg, 32);
+       val &= ~GENMASK(offset + size - 1, offset);
+       val |= strength << offset;
+
+       sh_pfc_write_reg(pfc, reg, 32, val);
+
+       spin_unlock_irqrestore(&pfc->lock, flags);
+
+       return 0;
+}
+
 /* Check whether the requested parameter is supported for a pin. */
 static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
                                    enum pin_config_param param)
@@ -493,6 +578,9 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
        case PIN_CONFIG_BIAS_PULL_DOWN:
                return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN;
 
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               return pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH;
+
        case PIN_CONFIG_POWER_SOURCE:
                return pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE;
 
@@ -532,6 +620,17 @@ static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin,
                break;
        }
 
+       case PIN_CONFIG_DRIVE_STRENGTH: {
+               int ret;
+
+               ret = sh_pfc_pinconf_get_drive_strength(pfc, _pin);
+               if (ret < 0)
+                       return ret;
+
+               *config = ret;
+               break;
+       }
+
        case PIN_CONFIG_POWER_SOURCE: {
                int ret;
 
@@ -584,6 +683,18 @@ static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin,
 
                        break;
 
+               case PIN_CONFIG_DRIVE_STRENGTH: {
+                       unsigned int arg =
+                               pinconf_to_config_argument(configs[i]);
+                       int ret;
+
+                       ret = sh_pfc_pinconf_set_drive_strength(pfc, _pin, arg);
+                       if (ret < 0)
+                               return ret;
+
+                       break;
+               }
+
                case PIN_CONFIG_POWER_SOURCE: {
                        unsigned int arg =
                                pinconf_to_config_argument(configs[i]);
index 87140790d1f1e1fd8288013cbebbd6a0532f20bf..656ea32f776c92ed93683ab93603c2dde0b1b189 100644 (file)
@@ -28,6 +28,7 @@ enum {
 #define SH_PFC_PIN_CFG_PULL_UP         (1 << 2)
 #define SH_PFC_PIN_CFG_PULL_DOWN       (1 << 3)
 #define SH_PFC_PIN_CFG_IO_VOLTAGE      (1 << 4)
+#define SH_PFC_PIN_CFG_DRIVE_STRENGTH  (1 << 5)
 #define SH_PFC_PIN_CFG_NO_GPIO         (1 << 31)
 
 struct sh_pfc_pin {
@@ -131,6 +132,21 @@ struct pinmux_cfg_reg {
                { var_fw0, var_fwn, 0 }, \
        .enum_ids = (const u16 [])
 
+struct pinmux_drive_reg_field {
+       u16 pin;
+       u8 offset;
+       u8 size;
+};
+
+struct pinmux_drive_reg {
+       u32 reg;
+       const struct pinmux_drive_reg_field fields[8];
+};
+
+#define PINMUX_DRIVE_REG(name, r) \
+       .reg = r, \
+       .fields =
+
 struct pinmux_data_reg {
        u32 reg;
        u8 reg_width;
@@ -199,6 +215,7 @@ struct sh_pfc_soc_info {
 #endif
 
        const struct pinmux_cfg_reg *cfg_regs;
+       const struct pinmux_drive_reg *drive_regs;
        const struct pinmux_data_reg *data_regs;
 
        const u16 *pinmux_data;