]> Pileus Git - ~andy/linux/blobdiff - drivers/pinctrl/pinctrl-abx500.c
HID: hidraw: fix improper mutex release
[~andy/linux] / drivers / pinctrl / pinctrl-abx500.c
index 6d4532702f809b5cc52629fbdc7ad238412153f3..1d3f988c2c8bff0cfff3e5fdebba241deaaff9d4 100644 (file)
 #include <linux/pinctrl/pinmux.h>
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/machine.h>
 
 #include "pinctrl-abx500.h"
+#include "core.h"
+#include "pinconf.h"
 
 /*
  * The AB9540 and AB8540 GPIO support are extended versions
 #define AB8540_GPIOX_VBAT_START        51
 #define AB8540_GPIOX_VBAT_END  54
 
+#define ABX500_GPIO_INPUT      0
+#define ABX500_GPIO_OUTPUT     1
+
 struct abx500_pinctrl {
        struct device *dev;
        struct pinctrl_dev *pctldev;
        struct abx500_pinctrl_soc_data *soc;
        struct gpio_chip chip;
        struct ab8500 *parent;
-       struct mutex lock;
        struct abx500_gpio_irq_cluster *irq_cluster;
        int irq_cluster_size;
 };
@@ -129,8 +134,8 @@ static int abx500_gpio_get_bit(struct gpio_chip *chip, u8 reg,
 
        if (ret < 0)
                dev_err(pct->dev,
-                       "%s read reg =%x, offset=%x failed\n",
-                       __func__, reg, offset);
+                       "%s read reg =%x, offset=%x failed (%d)\n",
+                       __func__, reg, offset, ret);
 
        return ret;
 }
@@ -146,7 +151,8 @@ static int abx500_gpio_set_bits(struct gpio_chip *chip, u8 reg,
        ret = abx500_mask_and_set_register_interruptible(pct->dev,
                                AB8500_MISC, reg, BIT(pos), val << pos);
        if (ret < 0)
-               dev_err(pct->dev, "%s write failed\n", __func__);
+               dev_err(pct->dev, "%s write reg, %x offset %x failed (%d)\n",
+                               __func__, reg, offset, ret);
 
        return ret;
 }
@@ -160,12 +166,24 @@ static int abx500_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
        bool bit;
+       bool is_out;
+       u8 gpio_offset = offset - 1;
        int ret;
 
-       ret = abx500_gpio_get_bit(chip, AB8500_GPIO_IN1_REG,
-                                 offset, &bit);
+       ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG,
+                       gpio_offset, &is_out);
+       if (ret < 0)
+               goto out;
+
+       if (is_out)
+               ret = abx500_gpio_get_bit(chip, AB8500_GPIO_OUT1_REG,
+                               gpio_offset, &bit);
+       else
+               ret = abx500_gpio_get_bit(chip, AB8500_GPIO_IN1_REG,
+                               gpio_offset, &bit);
+out:
        if (ret < 0) {
-               dev_err(pct->dev, "%s failed\n", __func__);
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
                return ret;
        }
 
@@ -179,13 +197,14 @@ static void abx500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 
        ret = abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
        if (ret < 0)
-               dev_err(pct->dev, "%s write failed\n", __func__);
+               dev_err(pct->dev, "%s write failed (%d)\n", __func__, ret);
 }
 
-static int abx500_config_pull_updown(struct abx500_pinctrl *pct,
-                                    int offset, enum abx500_gpio_pull_updown val)
+static int abx500_get_pull_updown(struct abx500_pinctrl *pct, int offset,
+                                 enum abx500_gpio_pull_updown *pull_updown)
 {
        u8 pos;
+       u8 val;
        int ret;
        struct pullud *pullud;
 
@@ -204,7 +223,41 @@ static int abx500_config_pull_updown(struct abx500_pinctrl *pct,
                goto out;
        }
 
-       pos = offset << 1;
+       ret = abx500_get_register_interruptible(pct->dev,
+                       AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG, &val);
+
+       pos = (offset - pullud->first_pin) << 1;
+       *pull_updown = (val >> pos) & AB8540_GPIO_PULL_UPDOWN_MASK;
+
+out:
+       if (ret < 0)
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
+
+       return ret;
+}
+
+static int abx500_set_pull_updown(struct abx500_pinctrl *pct,
+                                 int offset, enum abx500_gpio_pull_updown val)
+{
+       u8 pos;
+       int ret;
+       struct pullud *pullud;
+
+       if (!pct->soc->pullud) {
+               dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature",
+                               __func__);
+               ret = -EPERM;
+               goto out;
+       }
+
+       pullud = pct->soc->pullud;
+
+       if ((offset < pullud->first_pin)
+               || (offset > pullud->last_pin)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       pos = (offset - pullud->first_pin) << 1;
 
        ret = abx500_mask_and_set_register_interruptible(pct->dev,
                        AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG,
@@ -217,33 +270,51 @@ out:
        return ret;
 }
 
+static bool abx500_pullud_supported(struct gpio_chip *chip, unsigned gpio)
+{
+       struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+       struct pullud *pullud = pct->soc->pullud;
+
+       return (pullud &&
+               gpio >= pullud->first_pin &&
+               gpio <= pullud->last_pin);
+}
+
 static int abx500_gpio_direction_output(struct gpio_chip *chip,
                                        unsigned offset,
                                        int val)
 {
        struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
-       struct pullud *pullud = pct->soc->pullud;
        unsigned gpio;
        int ret;
 
        /* set direction as output */
-       ret = abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1);
+       ret = abx500_gpio_set_bits(chip,
+                               AB8500_GPIO_DIR1_REG,
+                               offset,
+                               ABX500_GPIO_OUTPUT);
        if (ret < 0)
-               return ret;
+               goto out;
 
        /* disable pull down */
-       ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1);
+       ret = abx500_gpio_set_bits(chip,
+                               AB8500_GPIO_PUD1_REG,
+                               offset,
+                               ABX500_GPIO_PULL_NONE);
        if (ret < 0)
-               return ret;
+               goto out;
 
        /* if supported, disable both pull down and pull up */
        gpio = offset + 1;
-       if (pullud && gpio >= pullud->first_pin && gpio <= pullud->last_pin) {
-               ret = abx500_config_pull_updown(pct,
+       if (abx500_pullud_supported(chip, gpio)) {
+               ret = abx500_set_pull_updown(pct,
                                gpio,
                                ABX500_GPIO_PULL_NONE);
-               if (ret < 0)
-                       return ret;
+       }
+out:
+       if (ret < 0) {
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
+               return ret;
        }
 
        /* set the output as 1 or 0 */
@@ -253,7 +324,10 @@ static int abx500_gpio_direction_output(struct gpio_chip *chip,
 static int abx500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 {
        /* set the register as input */
-       return abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0);
+       return abx500_gpio_set_bits(chip,
+                               AB8500_GPIO_DIR1_REG,
+                               offset,
+                               ABX500_GPIO_INPUT);
 }
 
 static int abx500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -338,10 +412,16 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
                if (af.alt_bit1 != UNUSED) {
                        ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
                                        offset, 0);
+                       if (ret < 0)
+                               goto out;
+
                        ret = abx500_gpio_set_bits(chip,
                                        AB8500_GPIO_ALTFUN_REG,
                                        af.alt_bit1,
                                        !!(af.alta_val && BIT(0)));
+                       if (ret < 0)
+                               goto out;
+
                        if (af.alt_bit2 != UNUSED)
                                ret = abx500_gpio_set_bits(chip,
                                        AB8500_GPIO_ALTFUN_REG,
@@ -355,8 +435,14 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
        case ABX500_ALT_B:
                ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
                                offset, 0);
+               if (ret < 0)
+                       goto out;
+
                ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
                                af.alt_bit1, !!(af.altb_val && BIT(0)));
+               if (ret < 0)
+                       goto out;
+
                if (af.alt_bit2 != UNUSED)
                        ret = abx500_gpio_set_bits(chip,
                                        AB8500_GPIO_ALTFUN_REG,
@@ -367,8 +453,14 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
        case ABX500_ALT_C:
                ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
                                offset, 0);
+               if (ret < 0)
+                       goto out;
+
                ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
                                af.alt_bit2, !!(af.altc_val && BIT(0)));
+               if (ret < 0)
+                       goto out;
+
                ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
                                af.alt_bit2, !!(af.altc_val && BIT(1)));
                break;
@@ -378,11 +470,14 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
 
                return -EINVAL;
        }
+out:
+       if (ret < 0)
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
 
        return ret;
 }
 
-static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+static int abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
                          unsigned gpio)
 {
        u8 mode;
@@ -393,6 +488,7 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
        struct alternate_functions af = pct->soc->alternate_functions[gpio];
        /* on ABx5xx, there is no GPIO0, so adjust the offset */
        unsigned offset = gpio - 1;
+       int ret;
 
        /*
         * if gpiosel_bit is set to unused,
@@ -402,8 +498,11 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
                return ABX500_DEFAULT;
 
        /* read GpioSelx register */
-       abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (offset / 8),
+       ret = abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (offset / 8),
                        af.gpiosel_bit, &bit_mode);
+       if (ret < 0)
+               goto out;
+
        mode = bit_mode;
 
        /* sanity check */
@@ -435,14 +534,19 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
         * pin use the AlternatFunction register
         * read alt_bit1 value
         */
-       abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG,
+       ret = abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG,
                            af.alt_bit1, &alt_bit1);
+       if (ret < 0)
+               goto out;
 
-       if (af.alt_bit2 != UNUSED)
+       if (af.alt_bit2 != UNUSED) {
                /* read alt_bit2 value */
-               abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2,
+               ret = abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG,
+                               af.alt_bit2,
                                &alt_bit2);
-       else
+               if (ret < 0)
+                       goto out;
+       } else
                alt_bit2 = 0;
 
        mode = (alt_bit2 << 1) + alt_bit1;
@@ -452,6 +556,10 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
                return ABX500_ALT_B;
        else
                return ABX500_ALT_C;
+
+out:
+       dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
+       return ret;
 }
 
 #ifdef CONFIG_DEBUG_FS
@@ -463,11 +571,14 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s,
                                     struct gpio_chip *chip,
                                     unsigned offset, unsigned gpio)
 {
+       struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
        const char *label = gpiochip_is_requested(chip, offset - 1);
        u8 gpio_offset = offset - 1;
        int mode = -1;
        bool is_out;
-       bool pull;
+       bool pd;
+       enum abx500_gpio_pull_updown pud = 0;
+       int ret;
 
        const char *modes[] = {
                [ABX500_DEFAULT]        = "default",
@@ -476,21 +587,48 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s,
                [ABX500_ALT_C]          = "altC",
        };
 
-       abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out);
-       abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG, gpio_offset, &pull);
+       const char *pull_up_down[] = {
+               [ABX500_GPIO_PULL_DOWN]         = "pull down",
+               [ABX500_GPIO_PULL_NONE]         = "pull none",
+               [ABX500_GPIO_PULL_NONE + 1]     = "pull none",
+               [ABX500_GPIO_PULL_UP]           = "pull up",
+       };
+
+       ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG,
+                       gpio_offset, &is_out);
+       if (ret < 0)
+               goto out;
+
+       seq_printf(s, " gpio-%-3d (%-20.20s) %-3s",
+                  gpio, label ?: "(none)",
+                  is_out ? "out" : "in ");
+
+       if (!is_out) {
+               if (abx500_pullud_supported(chip, offset)) {
+                       ret = abx500_get_pull_updown(pct, offset, &pud);
+                       if (ret < 0)
+                               goto out;
+
+                       seq_printf(s, " %-9s", pull_up_down[pud]);
+               } else {
+                       ret = abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG,
+                                       gpio_offset, &pd);
+                       if (ret < 0)
+                               goto out;
+
+                       seq_printf(s, " %-9s", pull_up_down[pd]);
+               }
+       } else
+               seq_printf(s, " %-9s", chip->get(chip, offset) ? "hi" : "lo");
 
        if (pctldev)
                mode = abx500_get_mode(pctldev, chip, offset);
 
-       seq_printf(s, " gpio-%-3d (%-20.20s) %-3s %-9s %s",
-                  gpio, label ?: "(none)",
-                  is_out ? "out" : "in ",
-                  is_out ?
-                  (chip->get
-                  ? (chip->get(chip, offset) ? "hi" : "lo")
-                  : "?  ")
-                  : (pull ? "pull up" : "pull down"),
-                  (mode < 0) ? "unknown" : modes[mode]);
+       seq_printf(s, " %s", (mode < 0) ? "unknown" : modes[mode]);
+
+out:
+       if (ret < 0)
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
 }
 
 static void abx500_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
@@ -594,6 +732,9 @@ static int abx500_pmx_enable(struct pinctrl_dev *pctldev, unsigned function,
                ret = abx500_set_mode(pctldev, chip, g->pins[i], g->altsetting);
        }
 
+       if (ret < 0)
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
+
        return ret;
 }
 
@@ -642,10 +783,8 @@ static int abx500_gpio_request_enable(struct pinctrl_dev *pctldev,
 
        ret = abx500_set_mode(pct->pctldev, &pct->chip,
                              offset, p->altfunc);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(pct->dev, "%s setting altfunc failed\n", __func__);
-               return ret;
-       }
 
        return ret;
 }
@@ -704,11 +843,193 @@ static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev,
                                 chip->base + offset - 1);
 }
 
+static void abx500_dt_free_map(struct pinctrl_dev *pctldev,
+               struct pinctrl_map *map, unsigned num_maps)
+{
+       int i;
+
+       for (i = 0; i < num_maps; i++)
+               if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
+                       kfree(map[i].data.configs.configs);
+       kfree(map);
+}
+
+static int abx500_dt_reserve_map(struct pinctrl_map **map,
+               unsigned *reserved_maps,
+               unsigned *num_maps,
+               unsigned reserve)
+{
+       unsigned old_num = *reserved_maps;
+       unsigned new_num = *num_maps + reserve;
+       struct pinctrl_map *new_map;
+
+       if (old_num >= new_num)
+               return 0;
+
+       new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
+       if (!new_map)
+               return -ENOMEM;
+
+       memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
+
+       *map = new_map;
+       *reserved_maps = new_num;
+
+       return 0;
+}
+
+static int abx500_dt_add_map_mux(struct pinctrl_map **map,
+               unsigned *reserved_maps,
+               unsigned *num_maps, const char *group,
+               const char *function)
+{
+       if (*num_maps == *reserved_maps)
+               return -ENOSPC;
+
+       (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
+       (*map)[*num_maps].data.mux.group = group;
+       (*map)[*num_maps].data.mux.function = function;
+       (*num_maps)++;
+
+       return 0;
+}
+
+static int abx500_dt_add_map_configs(struct pinctrl_map **map,
+               unsigned *reserved_maps,
+               unsigned *num_maps, const char *group,
+               unsigned long *configs, unsigned num_configs)
+{
+       unsigned long *dup_configs;
+
+       if (*num_maps == *reserved_maps)
+               return -ENOSPC;
+
+       dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
+                             GFP_KERNEL);
+       if (!dup_configs)
+               return -ENOMEM;
+
+       (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN;
+
+       (*map)[*num_maps].data.configs.group_or_pin = group;
+       (*map)[*num_maps].data.configs.configs = dup_configs;
+       (*map)[*num_maps].data.configs.num_configs = num_configs;
+       (*num_maps)++;
+
+       return 0;
+}
+
+static const char *abx500_find_pin_name(struct pinctrl_dev *pctldev,
+                                       const char *pin_name)
+{
+       int i, pin_number;
+       struct abx500_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
+
+       if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1)
+               for (i = 0; i < npct->soc->npins; i++)
+                       if (npct->soc->pins[i].number == pin_number)
+                               return npct->soc->pins[i].name;
+       return NULL;
+}
+
+static int abx500_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+               struct device_node *np,
+               struct pinctrl_map **map,
+               unsigned *reserved_maps,
+               unsigned *num_maps)
+{
+       int ret;
+       const char *function = NULL;
+       unsigned long *configs;
+       unsigned int nconfigs = 0;
+       bool has_config = 0;
+       unsigned reserve = 0;
+       struct property *prop;
+       const char *group, *gpio_name;
+       struct device_node *np_config;
+
+       ret = of_property_read_string(np, "ste,function", &function);
+       if (ret >= 0)
+               reserve = 1;
+
+       ret = pinconf_generic_parse_dt_config(np, &configs, &nconfigs);
+       if (nconfigs)
+               has_config = 1;
+
+       np_config = of_parse_phandle(np, "ste,config", 0);
+       if (np_config) {
+               ret = pinconf_generic_parse_dt_config(np_config, &configs,
+                               &nconfigs);
+               if (ret)
+                       goto exit;
+               has_config |= nconfigs;
+       }
+
+       ret = of_property_count_strings(np, "ste,pins");
+       if (ret < 0)
+               goto exit;
+
+       if (has_config)
+               reserve++;
+
+       reserve *= ret;
+
+       ret = abx500_dt_reserve_map(map, reserved_maps, num_maps, reserve);
+       if (ret < 0)
+               goto exit;
+
+       of_property_for_each_string(np, "ste,pins", prop, group) {
+               if (function) {
+                       ret = abx500_dt_add_map_mux(map, reserved_maps,
+                                       num_maps, group, function);
+                       if (ret < 0)
+                               goto exit;
+               }
+               if (has_config) {
+                       gpio_name = abx500_find_pin_name(pctldev, group);
+
+                       ret = abx500_dt_add_map_configs(map, reserved_maps,
+                                       num_maps, gpio_name, configs, 1);
+                       if (ret < 0)
+                               goto exit;
+               }
+
+       }
+exit:
+       return ret;
+}
+
+static int abx500_dt_node_to_map(struct pinctrl_dev *pctldev,
+                                struct device_node *np_config,
+                                struct pinctrl_map **map, unsigned *num_maps)
+{
+       unsigned reserved_maps;
+       struct device_node *np;
+       int ret;
+
+       reserved_maps = 0;
+       *map = NULL;
+       *num_maps = 0;
+
+       for_each_child_of_node(np_config, np) {
+               ret = abx500_dt_subnode_to_map(pctldev, np, map,
+                               &reserved_maps, num_maps);
+               if (ret < 0) {
+                       abx500_dt_free_map(pctldev, *map, *num_maps);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static const struct pinctrl_ops abx500_pinctrl_ops = {
        .get_groups_count = abx500_get_groups_cnt,
        .get_group_name = abx500_get_group_name,
        .get_group_pins = abx500_get_group_pins,
        .pin_dbg_show = abx500_pin_dbg_show,
+       .dt_node_to_map = abx500_dt_node_to_map,
+       .dt_free_map = abx500_dt_free_map,
 };
 
 static int abx500_pin_config_get(struct pinctrl_dev *pctldev,
@@ -723,10 +1044,9 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev,
                          unsigned long config)
 {
        struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
-       struct pullud *pullud = pct->soc->pullud;
        struct gpio_chip *chip = &pct->chip;
        unsigned offset;
-       int ret;
+       int ret = -EINVAL;
        enum pin_config_param param = pinconf_to_config_param(config);
        enum pin_config_param argument = pinconf_to_config_argument(config);
 
@@ -739,41 +1059,83 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev,
        offset = pin - 1;
 
        switch (param) {
-       case PIN_CONFIG_BIAS_PULL_DOWN:
+       case PIN_CONFIG_BIAS_DISABLE:
+               ret = abx500_gpio_direction_input(chip, offset);
+               if (ret < 0)
+                       goto out;
                /*
-                * if argument = 1 set the pull down
-                * else clear the pull down
+                * Some chips only support pull down, while some actually
+                * support both pull up and pull down. Such chips have
+                * a "pullud" range specified for the pins that support
+                * both features. If the pin is not within that range, we
+                * fall back to the old bit set that only support pull down.
                 */
+               if (abx500_pullud_supported(chip, pin))
+                       ret = abx500_set_pull_updown(pct,
+                               pin,
+                               ABX500_GPIO_PULL_NONE);
+               else
+                       /* Chip only supports pull down */
+                       ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG,
+                               offset, ABX500_GPIO_PULL_NONE);
+               break;
+
+       case PIN_CONFIG_BIAS_PULL_DOWN:
                ret = abx500_gpio_direction_input(chip, offset);
+               if (ret < 0)
+                       goto out;
                /*
+                * if argument = 1 set the pull down
+                * else clear the pull down
                 * Some chips only support pull down, while some actually
                 * support both pull up and pull down. Such chips have
                 * a "pullud" range specified for the pins that support
                 * both features. If the pin is not within that range, we
                 * fall back to the old bit set that only support pull down.
                 */
-               if (pullud &&
-                   pin >= pullud->first_pin &&
-                   pin <= pullud->last_pin)
-                       ret = abx500_config_pull_updown(pct,
+               if (abx500_pullud_supported(chip, pin))
+                       ret = abx500_set_pull_updown(pct,
                                pin,
                                argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE);
                else
                        /* Chip only supports pull down */
                        ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG,
-                               offset, argument ? 0 : 1);
+                               offset,
+                               argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE);
+               break;
+
+       case PIN_CONFIG_BIAS_PULL_UP:
+               ret = abx500_gpio_direction_input(chip, offset);
+               if (ret < 0)
+                       goto out;
+               /*
+                * if argument = 1 set the pull up
+                * else clear the pull up
+                */
+               ret = abx500_gpio_direction_input(chip, offset);
+               /*
+                * Some chips only support pull down, while some actually
+                * support both pull up and pull down. Such chips have
+                * a "pullud" range specified for the pins that support
+                * both features. If the pin is not within that range, do
+                * nothing
+                */
+               if (abx500_pullud_supported(chip, pin))
+                       ret = abx500_set_pull_updown(pct,
+                               pin,
+                               argument ? ABX500_GPIO_PULL_UP : ABX500_GPIO_PULL_NONE);
                break;
 
        case PIN_CONFIG_OUTPUT:
                ret = abx500_gpio_direction_output(chip, offset, argument);
-
                break;
 
        default:
                dev_err(chip->dev, "illegal configuration requested\n");
-
-               return -EINVAL;
        }
+out:
+       if (ret < 0)
+               dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
 
        return ret;
 }
@@ -881,9 +1243,6 @@ static int abx500_gpio_probe(struct platform_device *pdev)
                        id = (unsigned long)match->data;
        }
 
-       /* initialize the lock */
-       mutex_init(&pct->lock);
-
        /* Poke in other ASIC variants here */
        switch (id) {
        case PINCTRL_AB8500:
@@ -900,13 +1259,11 @@ static int abx500_gpio_probe(struct platform_device *pdev)
                break;
        default:
                dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n", id);
-               mutex_destroy(&pct->lock);
                return -EINVAL;
        }
 
        if (!pct->soc) {
                dev_err(&pdev->dev, "Invalid SOC data\n");
-               mutex_destroy(&pct->lock);
                return -EINVAL;
        }
 
@@ -917,7 +1274,6 @@ static int abx500_gpio_probe(struct platform_device *pdev)
        ret = gpiochip_add(&pct->chip);
        if (ret) {
                dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
-               mutex_destroy(&pct->lock);
                return ret;
        }
        dev_info(&pdev->dev, "added gpiochip\n");
@@ -954,7 +1310,6 @@ out_rem_chip:
        if (err)
                dev_info(&pdev->dev, "failed to remove gpiochip\n");
 
-       mutex_destroy(&pct->lock);
        return ret;
 }
 
@@ -974,8 +1329,6 @@ static int abx500_gpio_remove(struct platform_device *pdev)
                return ret;
        }
 
-       mutex_destroy(&pct->lock);
-
        return 0;
 }