{"id":818616,"url":"http://patchwork.ozlabs.org/api/patches/818616/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-gpio/patch/6b89df1bf07dac2ab295fca5fdf0e55179c47ed6.1506428208.git-series.quentin.schulz@free-electrons.com/","project":{"id":42,"url":"http://patchwork.ozlabs.org/api/projects/42/?format=json","name":"Linux GPIO development","link_name":"linux-gpio","list_id":"linux-gpio.vger.kernel.org","list_email":"linux-gpio@vger.kernel.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<6b89df1bf07dac2ab295fca5fdf0e55179c47ed6.1506428208.git-series.quentin.schulz@free-electrons.com>","list_archive_url":null,"date":"2017-09-26T12:17:12","name":"[v2,02/10] pinctrl: axp209: add pinctrl features","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"772f5aec2608186723ec9d96921bb3f12285a42e","submitter":{"id":69366,"url":"http://patchwork.ozlabs.org/api/people/69366/?format=json","name":"Quentin Schulz","email":"quentin.schulz@free-electrons.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-gpio/patch/6b89df1bf07dac2ab295fca5fdf0e55179c47ed6.1506428208.git-series.quentin.schulz@free-electrons.com/mbox/","series":[{"id":5123,"url":"http://patchwork.ozlabs.org/api/series/5123/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-gpio/list/?series=5123","date":"2017-09-26T12:17:17","name":"add pinmuxing support for pins in AXP209 and AXP813 PMICs","version":2,"mbox":"http://patchwork.ozlabs.org/series/5123/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/818616/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/818616/checks/","tags":{},"related":[],"headers":{"Return-Path":"<linux-gpio-owner@vger.kernel.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=linux-gpio-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y1g4v4pjfz9tXK\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 26 Sep 2017 22:19:59 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S966193AbdIZMT6 (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tTue, 26 Sep 2017 08:19:58 -0400","from mail.free-electrons.com ([62.4.15.54]:52099 \"EHLO\n\tmail.free-electrons.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S966178AbdIZMSH (ORCPT\n\t<rfc822; linux-gpio@vger.kernel.org>); Tue, 26 Sep 2017 08:18:07 -0400","by mail.free-electrons.com (Postfix, from userid 110)\n\tid E735E2083C; Tue, 26 Sep 2017 14:18:04 +0200 (CEST)","from localhost.localdomain\n\t(LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87])\n\tby mail.free-electrons.com (Postfix) with ESMTPSA id 8744220846;\n\tTue, 26 Sep 2017 14:17:54 +0200 (CEST)"],"X-Spam-Checker-Version":"SpamAssassin 3.4.0 (2014-02-07) on\n\tmail.free-electrons.com","X-Spam-Level":"","X-Spam-Status":"No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT,\n\tURIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0","From":"Quentin Schulz <quentin.schulz@free-electrons.com>","To":"linus.walleij@linaro.org, robh+dt@kernel.org, mark.rutland@arm.com,\n\twens@csie.org, linux@armlinux.org.uk,\n\tmaxime.ripard@free-electrons.com, lee.jones@linaro.org","Cc":"linux-gpio@vger.kernel.org, devicetree@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,\n\tlinux-sunxi@googlegroups.com, thomas.petazzoni@free-electrons.com,\n\tQuentin Schulz <quentin.schulz@free-electrons.com>","Subject":"[PATCH v2 02/10] pinctrl: axp209: add pinctrl features","Date":"Tue, 26 Sep 2017 14:17:12 +0200","Message-Id":"<6b89df1bf07dac2ab295fca5fdf0e55179c47ed6.1506428208.git-series.quentin.schulz@free-electrons.com>","X-Mailer":"git-send-email 2.11.0","In-Reply-To":["<cover.1c314f4154a6d27354625f03d0a5269eee55a9c5.1506428208.git-series.quentin.schulz@free-electrons.com>","<cover.1c314f4154a6d27354625f03d0a5269eee55a9c5.1506428208.git-series.quentin.schulz@free-electrons.com>"],"References":["<cover.1c314f4154a6d27354625f03d0a5269eee55a9c5.1506428208.git-series.quentin.schulz@free-electrons.com>","<cover.1c314f4154a6d27354625f03d0a5269eee55a9c5.1506428208.git-series.quentin.schulz@free-electrons.com>"],"Sender":"linux-gpio-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<linux-gpio.vger.kernel.org>","X-Mailing-List":"linux-gpio@vger.kernel.org"},"content":"The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,\nan ADC or a LDO regulator. GPIO2 can only act as a GPIO.\n\nThis adds the pinctrl features to the driver so GPIO0/1 can be used as\nADC or LDO regulator.\n\nSigned-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>\n---\n Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt |  28 +-\n drivers/pinctrl/pinctrl-axp209.c                             | 462 ++++++-\n 2 files changed, 468 insertions(+), 22 deletions(-)","diff":"diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt\nindex a661130..a5bfe87 100644\n--- a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt\n+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt\n@@ -1,4 +1,4 @@\n-AXP209 GPIO controller\n+AXP209 GPIO & pinctrl controller\n \n This driver follows the usual GPIO bindings found in\n Documentation/devicetree/bindings/gpio/gpio.txt\n@@ -28,3 +28,29 @@ axp209: pmic@34 {\n \t\t#gpio-cells = <2>;\n \t};\n };\n+\n+The GPIOs can be muxed to other functions and therefore, must be a subnode of\n+axp_gpio.\n+\n+Example:\n+\n+&axp_gpio {\n+\tgpio0_adc: gpio0_adc {\n+\t\tpin = \"GPIO0\";\n+\t\tfunction = \"adc\";\n+\t};\n+};\n+\n+&example_node {\n+\tpinctrl-names = \"default\";\n+\tpinctrl-0 = <&gpio0_adc>;\n+};\n+\n+GPIOs and their functions\n+-------------------------\n+\n+GPIO\t|\tFunctions\n+------------------------\n+GPIO0\t|\tgpio_in, gpio_out, ldo, adc\n+GPIO1\t|\tgpio_in, gpio_out, ldo, adc\n+GPIO2\t|\tgpio_in, gpio_out\ndiff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c\nindex 4a346b7..b35e8dd 100644\n--- a/drivers/pinctrl/pinctrl-axp209.c\n+++ b/drivers/pinctrl/pinctrl-axp209.c\n@@ -1,7 +1,8 @@\n /*\n- * AXP20x GPIO driver\n+ * AXP20x pinctrl and GPIO driver\n  *\n  * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>\n+ * Copyright (C) 201 Quentin Schulz <quentin.schulz@free-electrons.com>\n  *\n  * This program is free software; you can redistribute it and/or modify it\n  * under  the terms of the GNU General  Public License as published by the\n@@ -22,14 +23,92 @@\n #include <linux/regmap.h>\n #include <linux/slab.h>\n \n+#include <linux/pinctrl/pinctrl.h>\n+#include <linux/pinctrl/pinmux.h>\n+#include <linux/pinctrl/pinconf-generic.h>\n+\n #define AXP20X_GPIO_FUNCTIONS\t\t0x7\n #define AXP20X_GPIO_FUNCTION_OUT_LOW\t0\n #define AXP20X_GPIO_FUNCTION_OUT_HIGH\t1\n #define AXP20X_GPIO_FUNCTION_INPUT\t2\n \n+#define AXP20X_PINCTRL_PIN(_pin_num, _pin)\t\t\t\\\n+\t{\t\t\t\t\t\t\t\\\n+\t\t.number = _pin_num,\t\t\t\t\\\n+\t\t.name = _pin,\t\t\t\t\t\\\n+\t}\n+\n+#define AXP20X_PIN(_pin, ...)\t\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\\\n+\t\t.pin = _pin,\t\t\t\t\t\\\n+\t\t.functions = (struct axp20x_desc_function[]) {\t\\\n+\t\t\t      __VA_ARGS__, { } },\t\t\\\n+\t}\n+\n+#define AXP20X_FUNCTION(_val, _name)\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\\\n+\t\t.name = _name,\t\t\t\t\t\\\n+\t\t.muxval = _val,\t\t\t\t\t\\\n+\t}\n+\n+struct axp20x_desc_function {\n+\tconst char\t*name;\n+\tu8\t\tmuxval;\n+};\n+\n+struct axp20x_desc_pin {\n+\tstruct pinctrl_pin_desc\t\tpin;\n+\tstruct axp20x_desc_function\t*functions;\n+};\n+\n+struct axp20x_pinctrl_desc {\n+\tconst struct axp20x_desc_pin\t*pins;\n+\tint\t\t\t\tnpins;\n+};\n+\n+struct axp20x_pinctrl_group {\n+\tconst char\t*name;\n+\tunsigned long\tconfig;\n+\tunsigned int\tpin;\n+};\n+\n+struct axp20x_pinctrl_function {\n+\tconst char\t*name;\n+\tconst char\t**groups;\n+\tunsigned int\tngroups;\n+};\n+\n struct axp20x_gpio {\n \tstruct gpio_chip\tchip;\n \tstruct regmap\t\t*regmap;\n+\tstruct pinctrl_dev\t\t\t*pctl_dev;\n+\tstruct device\t\t\t\t*dev;\n+\tconst struct axp20x_pinctrl_desc\t*desc;\n+\tstruct axp20x_pinctrl_group\t\t*groups;\n+\tunsigned int\t\t\t\tngroups;\n+\tstruct axp20x_pinctrl_function\t\t*functions;\n+\tunsigned int\t\t\t\tnfunctions;\n+};\n+\n+static const struct axp20x_desc_pin axp209_pins[] = {\n+\tAXP20X_PIN(AXP20X_PINCTRL_PIN(0, \"GPIO0\"),\n+\t\t   AXP20X_FUNCTION(0x0, \"gpio_out\"),\n+\t\t   AXP20X_FUNCTION(0x2, \"gpio_in\"),\n+\t\t   AXP20X_FUNCTION(0x3, \"ldo\"),\n+\t\t   AXP20X_FUNCTION(0x4, \"adc\")),\n+\tAXP20X_PIN(AXP20X_PINCTRL_PIN(1, \"GPIO1\"),\n+\t\t   AXP20X_FUNCTION(0x0, \"gpio_out\"),\n+\t\t   AXP20X_FUNCTION(0x2, \"gpio_in\"),\n+\t\t   AXP20X_FUNCTION(0x3, \"ldo\"),\n+\t\t   AXP20X_FUNCTION(0x4, \"adc\")),\n+\tAXP20X_PIN(AXP20X_PINCTRL_PIN(2, \"GPIO2\"),\n+\t\t   AXP20X_FUNCTION(0x0, \"gpio_out\"),\n+\t\t   AXP20X_FUNCTION(0x2, \"gpio_in\")),\n+};\n+\n+static const struct axp20x_pinctrl_desc axp20x_pinctrl_data = {\n+\t.pins\t= axp209_pins,\n+\t.npins\t= ARRAY_SIZE(axp209_pins),\n };\n \n static int axp20x_gpio_get_reg(unsigned offset)\n@@ -48,16 +127,7 @@ static int axp20x_gpio_get_reg(unsigned offset)\n \n static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)\n {\n-\tstruct axp20x_gpio *gpio = gpiochip_get_data(chip);\n-\tint reg;\n-\n-\treg = axp20x_gpio_get_reg(offset);\n-\tif (reg < 0)\n-\t\treturn reg;\n-\n-\treturn regmap_update_bits(gpio->regmap, reg,\n-\t\t\t\t  AXP20X_GPIO_FUNCTIONS,\n-\t\t\t\t  AXP20X_GPIO_FUNCTION_INPUT);\n+\treturn pinctrl_gpio_direction_input(chip->base + offset);\n }\n \n static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)\n@@ -105,30 +175,334 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)\n static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,\n \t\t\t      int value)\n {\n+\tchip->set(chip, offset, value);\n+\n+\treturn 0;\n+}\n+\n+static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,\n+\t\t\t    int value)\n+{\n \tstruct axp20x_gpio *gpio = gpiochip_get_data(chip);\n \tint reg;\n \n \treg = axp20x_gpio_get_reg(offset);\n \tif (reg < 0)\n+\t\treturn;\n+\n+\tregmap_update_bits(gpio->regmap, reg,\n+\t\t\t   AXP20X_GPIO_FUNCTIONS,\n+\t\t\t   value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :\n+\t\t\t   AXP20X_GPIO_FUNCTION_OUT_LOW);\n+}\n+\n+static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset,\n+\t\t\t  u8 config)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\tint reg;\n+\n+\treg = axp20x_gpio_get_reg(offset);\n+\tif (reg < 0)\n \t\treturn reg;\n \n-\treturn regmap_update_bits(gpio->regmap, reg,\n-\t\t\t\t  AXP20X_GPIO_FUNCTIONS,\n-\t\t\t\t  value ? AXP20X_GPIO_FUNCTION_OUT_HIGH\n-\t\t\t\t  : AXP20X_GPIO_FUNCTION_OUT_LOW);\n+\treturn regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS,\n+\t\t\t\t  config);\n }\n \n-static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,\n-\t\t\t    int value)\n+static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\n+\treturn gpio->nfunctions;\n+}\n+\n+static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev,\n+\t\t\t\t\tunsigned int selector)\n {\n-\taxp20x_gpio_output(chip, offset, value);\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\n+\treturn gpio->functions[selector].name;\n+}\n+\n+static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,\n+\t\t\t\t  unsigned int selector,\n+\t\t\t\t  const char * const **groups,\n+\t\t\t\t  unsigned int *num_groups)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\n+\t*groups = gpio->functions[selector].groups;\n+\t*num_groups = gpio->functions[selector].ngroups;\n+\n+\treturn 0;\n+}\n+\n+static struct axp20x_desc_function *\n+axp20x_pinctrl_desc_find_func_by_name(struct axp20x_gpio *gpio,\n+\t\t\t\t      const char *group, const char *func)\n+{\n+\tconst struct axp20x_desc_pin *pin;\n+\tstruct axp20x_desc_function *desc_func;\n+\tint i;\n+\n+\tfor (i = 0; i < gpio->desc->npins; i++) {\n+\t\tpin = &gpio->desc->pins[i];\n+\n+\t\tif (!strcmp(pin->pin.name, group)) {\n+\t\t\tdesc_func = pin->functions;\n+\n+\t\t\twhile (desc_func->name) {\n+\t\t\t\tif (!strcmp(desc_func->name, func))\n+\t\t\t\t\treturn desc_func;\n+\t\t\t\tdesc_func++;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * Pins are uniquely named. Groups are named after one\n+\t\t\t * pin name. If one pin matches group name but its\n+\t\t\t * function cannot be found, no other pin will match\n+\t\t\t * group name.\n+\t\t\t */\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,\n+\t\t\t      unsigned int function, unsigned int group)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\tstruct axp20x_pinctrl_group *g = gpio->groups + group;\n+\tstruct axp20x_pinctrl_function *func = gpio->functions + function;\n+\tstruct axp20x_desc_function *desc_func =\n+\t\taxp20x_pinctrl_desc_find_func_by_name(gpio, g->name,\n+\t\t\t\t\t\t      func->name);\n+\tif (!desc_func)\n+\t\treturn -EINVAL;\n+\n+\treturn axp20x_pmx_set(pctldev, g->pin, desc_func->muxval);\n+}\n+\n+static struct axp20x_desc_function *\n+axp20x_pctl_desc_find_func_by_pin(struct axp20x_gpio *gpio, unsigned int offset,\n+\t\t\t\t  const char *func)\n+{\n+\tconst struct axp20x_desc_pin *pin;\n+\tstruct axp20x_desc_function *desc_func;\n+\tint i;\n+\n+\tfor (i = 0; i < gpio->desc->npins; i++) {\n+\t\tpin = &gpio->desc->pins[i];\n+\n+\t\tif (pin->pin.number == offset) {\n+\t\t\tdesc_func = pin->functions;\n+\n+\t\t\twhile (desc_func->name) {\n+\t\t\t\tif (!strcmp(desc_func->name, func))\n+\t\t\t\t\treturn desc_func;\n+\n+\t\t\t\tdesc_func++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,\n+\t\t\t\t\t struct pinctrl_gpio_range *range,\n+\t\t\t\t\t unsigned int offset, bool input)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\tstruct axp20x_desc_function *desc_func;\n+\tconst char *func;\n+\n+\tif (input)\n+\t\tfunc = \"gpio_in\";\n+\telse\n+\t\tfunc = \"gpio_out\";\n+\n+\tdesc_func = axp20x_pctl_desc_find_func_by_pin(gpio, offset, func);\n+\tif (!desc_func)\n+\t\treturn -EINVAL;\n+\n+\treturn axp20x_pmx_set(pctldev, offset, desc_func->muxval);\n+}\n+\n+static const struct pinmux_ops axp20x_pmx_ops = {\n+\t.get_functions_count\t= axp20x_pmx_func_cnt,\n+\t.get_function_name\t= axp20x_pmx_func_name,\n+\t.get_function_groups\t= axp20x_pmx_func_groups,\n+\t.set_mux\t\t= axp20x_pmx_set_mux,\n+\t.gpio_set_direction\t= axp20x_pmx_gpio_set_direction,\n+\t.strict\t\t\t= true,\n+};\n+\n+static int axp20x_groups_cnt(struct pinctrl_dev *pctldev)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\n+\treturn gpio->ngroups;\n+}\n+\n+static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,\n+\t\t\t     const unsigned int **pins, unsigned int *num_pins)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\tstruct axp20x_pinctrl_group *g = gpio->groups + selector;\n+\n+\t*pins = (unsigned int *)&g->pin;\n+\t*num_pins = 1;\n+\n+\treturn 0;\n+}\n+\n+static const char *axp20x_group_name(struct pinctrl_dev *pctldev,\n+\t\t\t\t     unsigned int selector)\n+{\n+\tstruct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);\n+\n+\treturn gpio->groups[selector].name;\n+}\n+\n+static const struct pinctrl_ops axp20x_pctrl_ops = {\n+\t.dt_node_to_map\t\t= pinconf_generic_dt_node_to_map_group,\n+\t.dt_free_map\t\t= pinconf_generic_dt_free_map,\n+\t.get_groups_count\t= axp20x_groups_cnt,\n+\t.get_group_name\t\t= axp20x_group_name,\n+\t.get_group_pins\t\t= axp20x_group_pins,\n+};\n+\n+static struct axp20x_pinctrl_function *\n+axp20x_pinctrl_function_by_name(struct axp20x_gpio *gpio, const char *name)\n+{\n+\tstruct axp20x_pinctrl_function *func = gpio->functions;\n+\n+\twhile (func->name) {\n+\t\tif (!strcmp(func->name, name))\n+\t\t\treturn func;\n+\t\tfunc++;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int axp20x_pinctrl_add_function(struct axp20x_gpio *gpio,\n+\t\t\t\t       const char *name)\n+{\n+\tstruct axp20x_pinctrl_function *func = gpio->functions;\n+\n+\twhile (func->name) {\n+\t\tif (!strcmp(func->name, name)) {\n+\t\t\tfunc->ngroups++;\n+\t\t\treturn -EEXIST;\n+\t\t}\n+\n+\t\tfunc++;\n+\t}\n+\n+\tfunc->name = name;\n+\tfunc->ngroups = 1;\n+\n+\tgpio->nfunctions++;\n+\n+\treturn 0;\n+}\n+\n+static int axp20x_attach_group_function(struct platform_device *pdev,\n+\t\t\t\t\tconst struct axp20x_desc_pin *pin)\n+{\n+\tstruct axp20x_gpio *gpio = platform_get_drvdata(pdev);\n+\tstruct axp20x_desc_function *desc_func = pin->functions;\n+\tstruct axp20x_pinctrl_function *func;\n+\tconst char **func_grp;\n+\n+\twhile (desc_func->name) {\n+\t\tfunc = axp20x_pinctrl_function_by_name(gpio, desc_func->name);\n+\t\tif (!func)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (!func->groups) {\n+\t\t\tfunc->groups = devm_kzalloc(&pdev->dev,\n+\t\t\t\t\t\t    func->ngroups * sizeof(const char *),\n+\t\t\t\t\t\t    GFP_KERNEL);\n+\t\t\tif (!func->groups)\n+\t\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\tfunc_grp = func->groups;\n+\t\twhile (*func_grp)\n+\t\t\tfunc_grp++;\n+\n+\t\t*func_grp = pin->pin.name;\n+\t\tdesc_func++;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int axp20x_build_state(struct platform_device *pdev)\n+{\n+\tstruct axp20x_gpio *gpio = platform_get_drvdata(pdev);\n+\tunsigned int npins = gpio->desc->npins;\n+\tconst struct axp20x_desc_pin *pin;\n+\tstruct axp20x_desc_function *func;\n+\tint i, ret;\n+\n+\tgpio->ngroups = npins;\n+\tgpio->groups = devm_kzalloc(&pdev->dev,\n+\t\t\t\t    gpio->ngroups * sizeof(*gpio->groups),\n+\t\t\t\t    GFP_KERNEL);\n+\tif (!gpio->groups)\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = 0; i < npins; i++) {\n+\t\tgpio->groups[i].name = gpio->desc->pins[i].pin.name;\n+\t\tgpio->groups[i].pin = gpio->desc->pins[i].pin.number;\n+\t}\n+\n+\t/* We assume 4 functions per pin should be enough as a default max */\n+\tgpio->functions = devm_kzalloc(&pdev->dev,\n+\t\t\t\t       npins * 4 * sizeof(*gpio->functions),\n+\t\t\t\t       GFP_KERNEL);\n+\tif (!gpio->functions)\n+\t\treturn -ENOMEM;\n+\n+\t/* Create a list of uniquely named functions */\n+\tfor (i = 0; i < npins; i++) {\n+\t\tpin = &gpio->desc->pins[i];\n+\t\tfunc = pin->functions;\n+\n+\t\twhile (func->name) {\n+\t\t\taxp20x_pinctrl_add_function(gpio, func->name);\n+\t\t\tfunc++;\n+\t\t}\n+\t}\n+\n+\tgpio->functions = krealloc(gpio->functions,\n+\t\t\t\t   gpio->nfunctions * sizeof(*gpio->functions),\n+\t\t\t\t   GFP_KERNEL);\n+\n+\tfor (i = 0; i < npins; i++) {\n+\t\tpin = &gpio->desc->pins[i];\n+\t\tret = axp20x_attach_group_function(pdev, pin);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n }\n \n static int axp20x_gpio_probe(struct platform_device *pdev)\n {\n \tstruct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);\n \tstruct axp20x_gpio *gpio;\n-\tint ret;\n+\tstruct pinctrl_desc *pctrl_desc;\n+\tstruct pinctrl_pin_desc *pins;\n+\tint ret, i;\n \n \tif (!of_device_is_available(pdev->dev.of_node))\n \t\treturn -ENODEV;\n@@ -144,6 +518,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev)\n \n \tgpio->chip.base\t\t\t= -1;\n \tgpio->chip.can_sleep\t\t= true;\n+\tgpio->chip.request\t\t= gpiochip_generic_request;\n+\tgpio->chip.free\t\t\t= gpiochip_generic_free;\n \tgpio->chip.parent\t\t= &pdev->dev;\n \tgpio->chip.label\t\t= dev_name(&pdev->dev);\n \tgpio->chip.owner\t\t= THIS_MODULE;\n@@ -156,13 +532,56 @@ static int axp20x_gpio_probe(struct platform_device *pdev)\n \n \tgpio->regmap = axp20x->regmap;\n \n+\tgpio->desc = &axp20x_pinctrl_data;\n+\tgpio->dev = &pdev->dev;\n+\n+\tplatform_set_drvdata(pdev, gpio);\n+\n+\tret = axp20x_build_state(pdev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tpins = devm_kzalloc(&pdev->dev, gpio->desc->npins * sizeof(*pins),\n+\t\t\t    GFP_KERNEL);\n+\tif (!pins)\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = 0; i < gpio->desc->npins; i++)\n+\t\tpins[i] = gpio->desc->pins[i].pin;\n+\n+\tpctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);\n+\tif (!pctrl_desc)\n+\t\treturn -ENOMEM;\n+\n+\tpctrl_desc->name = dev_name(&pdev->dev);\n+\tpctrl_desc->owner = THIS_MODULE;\n+\tpctrl_desc->pins = pins;\n+\tpctrl_desc->npins = gpio->desc->npins;\n+\tpctrl_desc->pctlops = &axp20x_pctrl_ops;\n+\tpctrl_desc->pmxops = &axp20x_pmx_ops;\n+\n+\tgpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio);\n+\tif (IS_ERR(gpio->pctl_dev)) {\n+\t\tdev_err(&pdev->dev, \"couldn't register pinctrl driver\\n\");\n+\t\treturn PTR_ERR(gpio->pctl_dev);\n+\t}\n+\n \tret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);\n \tif (ret) {\n \t\tdev_err(&pdev->dev, \"Failed to register GPIO chip\\n\");\n \t\treturn ret;\n \t}\n \n-\tdev_info(&pdev->dev, \"AXP209 GPIO driver loaded\\n\");\n+\tret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev),\n+\t\t\t\t     gpio->desc->pins->pin.number,\n+\t\t\t\t     gpio->desc->pins->pin.number,\n+\t\t\t\t     gpio->desc->npins);\n+\tif (ret) {\n+\t\tdev_err(&pdev->dev, \"failed to add pin range\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tdev_info(&pdev->dev, \"AXP209 pinctrl and GPIO driver loaded\\n\");\n \n \treturn 0;\n }\n@@ -184,5 +603,6 @@ static struct platform_driver axp20x_gpio_driver = {\n module_platform_driver(axp20x_gpio_driver);\n \n MODULE_AUTHOR(\"Maxime Ripard <maxime.ripard@free-electrons.com>\");\n-MODULE_DESCRIPTION(\"AXP20x PMIC GPIO driver\");\n+MODULE_AUTHOR(\"Quentin Schulz <quentin.schulz@free-electrons.com>\");\n+MODULE_DESCRIPTION(\"AXP20x PMIC pinctrl and GPIO driver\");\n MODULE_LICENSE(\"GPL\");\n","prefixes":["v2","02/10"]}