Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/818616/?format=api
{ "id": 818616, "url": "http://patchwork.ozlabs.org/api/patches/818616/?format=api", "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=api", "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=api", "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=api", "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" ] }