From: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
To: Quentin Schulz
<quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
wens-jdAy2FN1RRM@public.gmane.org,
linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org,
lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org,
linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
Subject: Re: [PATCH v3 02/12] pinctrl: axp209: add pinctrl features
Date: Mon, 2 Oct 2017 22:37:44 +0200 [thread overview]
Message-ID: <20171002203744.e2hhrntjclydvkpg@flea> (raw)
In-Reply-To: <20171002120854.5212-3-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 14207 bytes --]
On Mon, Oct 02, 2017 at 12:08:44PM +0000, Quentin Schulz wrote:
> The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
> an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
>
> This adds the pinctrl features to the driver so GPIO0/1 can be used as
> ADC or LDO regulator.
>
> Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
> .../devicetree/bindings/pinctrl/pinctrl-axp209.txt | 28 +-
> drivers/pinctrl/pinctrl-axp209.c | 293 +++++++++++++++++++--
> 2 files changed, 300 insertions(+), 21 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> index a6611304dd3c..388c04492afd 100644
> --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> @@ -1,4 +1,4 @@
> -AXP209 GPIO controller
> +AXP209 GPIO & pinctrl controller
>
> This driver follows the usual GPIO bindings found in
> Documentation/devicetree/bindings/gpio/gpio.txt
> @@ -28,3 +28,29 @@ axp209: pmic@34 {
> #gpio-cells = <2>;
> };
> };
> +
> +The GPIOs can be muxed to other functions and therefore, must be a subnode of
> +axp_gpio.
> +
> +Example:
> +
> +&axp_gpio {
> + gpio0_adc: gpio0_adc {
> + pins = "GPIO0";
> + function = "adc";
> + };
> +};
> +
> +&example_node {
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio0_adc>;
> +};
> +
> +GPIOs and their functions
> +-------------------------
> +
> +GPIO | Functions
> +------------------------
> +GPIO0 | gpio_in, gpio_out, ldo, adc
> +GPIO1 | gpio_in, gpio_out, ldo, adc
> +GPIO2 | gpio_in, gpio_out
> diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c
> index 4a346b7b4172..96ef0cc28762 100644
> --- a/drivers/pinctrl/pinctrl-axp209.c
> +++ b/drivers/pinctrl/pinctrl-axp209.c
> @@ -1,7 +1,8 @@
> /*
> - * AXP20x GPIO driver
> + * AXP20x pinctrl and GPIO driver
> *
> * Copyright (C) 2016 Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> + * Copyright (C) 2017 Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> *
> * This program is free software; you can redistribute it and/or modify it
> * under the terms of the GNU General Public License as published by the
> @@ -22,14 +23,57 @@
> #include <linux/regmap.h>
> #include <linux/slab.h>
>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +
You should order the headers by alphabetical order.
> #define AXP20X_GPIO_FUNCTIONS 0x7
> #define AXP20X_GPIO_FUNCTION_OUT_LOW 0
> #define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
> #define AXP20X_GPIO_FUNCTION_INPUT 2
>
> +#define AXP20X_FUNC_GPIO_OUT 0
> +#define AXP20X_FUNC_GPIO_IN 1
> +#define AXP20X_FUNC_LDO 2
> +#define AXP20X_FUNC_ADC 3
> +#define AXP20X_FUNCS_NB 4
> +
> +struct axp20x_pctrl_desc {
> + const struct pinctrl_pin_desc *pins;
> + unsigned int npins;
> + /* Stores the pins supporting LDO function. Bit offset is pin number. */
> + unsigned int ldo_mask;
> + /* Stores the pins supporting ADC function. Bit offset is pin number. */
> + unsigned int adc_mask;
You can use u8 here.
> +};
> +
> +struct axp20x_pinctrl_function {
> + const char *name;
> + unsigned int muxval;
> + const char **groups;
> + unsigned int ngroups;
> +};
> +
> struct axp20x_gpio {
> struct gpio_chip chip;
> struct regmap *regmap;
> + struct pinctrl_dev *pctl_dev;
> + struct device *dev;
> + const struct axp20x_pctrl_desc *desc;
> + struct axp20x_pinctrl_function funcs[AXP20X_FUNCS_NB];
> +};
> +
> +static const struct pinctrl_pin_desc axp209_pins[] = {
> + PINCTRL_PIN(0, "GPIO0"),
> + PINCTRL_PIN(1, "GPIO1"),
> + PINCTRL_PIN(2, "GPIO2"),
> +};
> +
> +static const struct axp20x_pctrl_desc axp20x_data = {
> + .pins = axp209_pins,
> + .npins = ARRAY_SIZE(axp209_pins),
> + .ldo_mask = BIT(0) | BIT(1),
> + .adc_mask = BIT(0) | BIT(1),
> };
>
> static int axp20x_gpio_get_reg(unsigned offset)
> @@ -48,16 +92,7 @@ static int axp20x_gpio_get_reg(unsigned offset)
>
> static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
> {
> - struct axp20x_gpio *gpio = gpiochip_get_data(chip);
> - int reg;
> -
> - reg = axp20x_gpio_get_reg(offset);
> - if (reg < 0)
> - return reg;
> -
> - return regmap_update_bits(gpio->regmap, reg,
> - AXP20X_GPIO_FUNCTIONS,
> - AXP20X_GPIO_FUNCTION_INPUT);
> + return pinctrl_gpio_direction_input(chip->base + offset);
> }
>
> static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
> @@ -105,29 +140,210 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
> static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
> int value)
> {
> + chip->set(chip, offset, value);
> +
> + return 0;
> +}
> +
> +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
> + int value)
> +{
> struct axp20x_gpio *gpio = gpiochip_get_data(chip);
> int reg;
>
> reg = axp20x_gpio_get_reg(offset);
> if (reg < 0)
> + return;
> +
> + regmap_update_bits(gpio->regmap, reg,
> + AXP20X_GPIO_FUNCTIONS,
> + value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :
> + AXP20X_GPIO_FUNCTION_OUT_LOW);
> +}
> +
> +static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset,
> + u8 config)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> + int reg;
> +
> + reg = axp20x_gpio_get_reg(offset);
> + if (reg < 0)
> return reg;
>
> - return regmap_update_bits(gpio->regmap, reg,
> - AXP20X_GPIO_FUNCTIONS,
> - value ? AXP20X_GPIO_FUNCTION_OUT_HIGH
> - : AXP20X_GPIO_FUNCTION_OUT_LOW);
> + return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS,
> + config);
> }
>
> -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
> - int value)
> +static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return ARRAY_SIZE(gpio->funcs);
> +}
> +
> +static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> {
> - axp20x_gpio_output(chip, offset, value);
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->funcs[selector].name;
> +}
> +
> +static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const char * const **groups,
> + unsigned int *num_groups)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + *groups = gpio->funcs[selector].groups;
> + *num_groups = gpio->funcs[selector].ngroups;
> +
> + return 0;
> +}
> +
> +static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int function, unsigned int group)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int mask;
> +
> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
> + if (function <= AXP20X_FUNC_GPIO_IN)
> + return axp20x_pmx_set(pctldev, group,
> + gpio->funcs[function].muxval);
> +
> + if (function == AXP20X_FUNC_LDO)
> + mask = gpio->desc->ldo_mask;
> + else
> + mask = gpio->desc->adc_mask;
> +
> + if (!(BIT(group) & mask))
> + return -EINVAL;
> +
> + return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval);
> +}
> +
> +static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int offset, bool input)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + if (input)
> + return axp20x_pmx_set(pctldev, offset,
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval);
> +
> + return axp20x_pmx_set(pctldev, offset,
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval);
> +}
> +
> +static const struct pinmux_ops axp20x_pmx_ops = {
> + .get_functions_count = axp20x_pmx_func_cnt,
> + .get_function_name = axp20x_pmx_func_name,
> + .get_function_groups = axp20x_pmx_func_groups,
> + .set_mux = axp20x_pmx_set_mux,
> + .gpio_set_direction = axp20x_pmx_gpio_set_direction,
> + .strict = true,
> +};
> +
> +static int axp20x_groups_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->desc->npins;
> +}
> +
> +static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
> + const unsigned int **pins, unsigned int *num_pins)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + *pins = (unsigned int *)&gpio->desc->pins[selector];
> + *num_pins = 1;
> +
> + return 0;
> +}
> +
> +static const char *axp20x_group_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->desc->pins[selector].name;
> +}
> +
> +static const struct pinctrl_ops axp20x_pctrl_ops = {
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> + .dt_free_map = pinconf_generic_dt_free_map,
> + .get_groups_count = axp20x_groups_cnt,
> + .get_group_name = axp20x_group_name,
> + .get_group_pins = axp20x_group_pins,
> +};
> +
> +static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask,
> + unsigned int mask_len,
> + struct axp20x_pinctrl_function *func,
> + const struct pinctrl_pin_desc *pins)
> +{
> + unsigned long int mask_cpy = mask;
> + const char **group;
> + unsigned int ngroups = hweight8(mask);
> + int bit;
> +
> + func->ngroups = ngroups;
> + if (func->ngroups > 0) {
> + func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *),
> + GFP_KERNEL);
> + group = func->groups;
> + for_each_set_bit(bit, &mask_cpy, mask_len) {
> + *group = pins[bit].name;
> + group++;
> + }
> + }
> +}
> +
> +static void axp20x_build_funcs_groups(struct platform_device *pdev)
> +{
> + struct axp20x_gpio *gpio = platform_get_drvdata(pdev);
> + int i, pin;
> +
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out";
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = 0x0;
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in";
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = 0x2;
Why do you need hexadecimal values here?
> + gpio->funcs[AXP20X_FUNC_LDO].name = "ldo";
> + gpio->funcs[AXP20X_FUNC_LDO].muxval = 0x3;
> + gpio->funcs[AXP20X_FUNC_ADC].name = "adc";
> + gpio->funcs[AXP20X_FUNC_ADC].muxval = 0x4;
And you probably can define that statically.
> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
> + for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) {
> + gpio->funcs[i].ngroups = gpio->desc->npins;
> + gpio->funcs[i].groups = devm_kzalloc(&pdev->dev,
> + gpio->desc->npins * sizeof(const char *),
> + GFP_KERNEL);
> + for (pin = 0; pin < gpio->desc->npins; pin++)
> + gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name;
> + }
> +
> + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask,
> + gpio->desc->npins,
> + &gpio->funcs[AXP20X_FUNC_LDO],
> + gpio->desc->pins);
> +
> + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask,
> + gpio->desc->npins,
> + &gpio->funcs[AXP20X_FUNC_ADC],
> + gpio->desc->pins);
> }
>
> static int axp20x_gpio_probe(struct platform_device *pdev)
> {
> struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> struct axp20x_gpio *gpio;
> + struct pinctrl_desc *pctrl_desc;
> int ret;
>
> if (!of_device_is_available(pdev->dev.of_node))
> @@ -144,6 +360,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
>
> gpio->chip.base = -1;
> gpio->chip.can_sleep = true;
> + gpio->chip.request = gpiochip_generic_request;
> + gpio->chip.free = gpiochip_generic_free;
> gpio->chip.parent = &pdev->dev;
> gpio->chip.label = dev_name(&pdev->dev);
> gpio->chip.owner = THIS_MODULE;
> @@ -154,15 +372,49 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
> gpio->chip.direction_output = axp20x_gpio_output;
> gpio->chip.ngpio = 3;
>
> + gpio->desc = &axp20x_data;
> +
> gpio->regmap = axp20x->regmap;
>
> + gpio->dev = &pdev->dev;
> +
Why do you need new lines between each affectation?
> + platform_set_drvdata(pdev, gpio);
> +
> + axp20x_build_funcs_groups(pdev);
> +
> + pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);
> + if (!pctrl_desc)
> + return -ENOMEM;
> +
> + pctrl_desc->name = dev_name(&pdev->dev);
> + pctrl_desc->owner = THIS_MODULE;
> + pctrl_desc->pins = gpio->desc->pins;
> + pctrl_desc->npins = gpio->desc->npins;
> + pctrl_desc->pctlops = &axp20x_pctrl_ops;
> + pctrl_desc->pmxops = &axp20x_pmx_ops;
> +
> + gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio);
> + if (IS_ERR(gpio->pctl_dev)) {
> + dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
> + return PTR_ERR(gpio->pctl_dev);
> + }
> +
> ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
> if (ret) {
> dev_err(&pdev->dev, "Failed to register GPIO chip\n");
> return ret;
> }
>
> - dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n");
> + ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev),
> + gpio->desc->pins->number,
> + gpio->desc->pins->number,
> + gpio->desc->npins);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to add pin range\n");
> + return ret;
> + }
> +
> + dev_info(&pdev->dev, "AXP209 pinctrl and GPIO driver loaded\n");
>
> return 0;
> }
> @@ -184,5 +436,6 @@ static struct platform_driver axp20x_gpio_driver = {
> module_platform_driver(axp20x_gpio_driver);
>
> MODULE_AUTHOR("Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
> -MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>");
> +MODULE_DESCRIPTION("AXP20x PMIC pinctrl and GPIO driver");
> MODULE_LICENSE("GPL");
Looks much better otherwise, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
WARNING: multiple messages have this Message-ID (diff)
From: maxime.ripard@free-electrons.com (Maxime Ripard)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 02/12] pinctrl: axp209: add pinctrl features
Date: Mon, 2 Oct 2017 22:37:44 +0200 [thread overview]
Message-ID: <20171002203744.e2hhrntjclydvkpg@flea> (raw)
In-Reply-To: <20171002120854.5212-3-quentin.schulz@free-electrons.com>
On Mon, Oct 02, 2017 at 12:08:44PM +0000, Quentin Schulz wrote:
> The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
> an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
>
> This adds the pinctrl features to the driver so GPIO0/1 can be used as
> ADC or LDO regulator.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> .../devicetree/bindings/pinctrl/pinctrl-axp209.txt | 28 +-
> drivers/pinctrl/pinctrl-axp209.c | 293 +++++++++++++++++++--
> 2 files changed, 300 insertions(+), 21 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> index a6611304dd3c..388c04492afd 100644
> --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> @@ -1,4 +1,4 @@
> -AXP209 GPIO controller
> +AXP209 GPIO & pinctrl controller
>
> This driver follows the usual GPIO bindings found in
> Documentation/devicetree/bindings/gpio/gpio.txt
> @@ -28,3 +28,29 @@ axp209: pmic at 34 {
> #gpio-cells = <2>;
> };
> };
> +
> +The GPIOs can be muxed to other functions and therefore, must be a subnode of
> +axp_gpio.
> +
> +Example:
> +
> +&axp_gpio {
> + gpio0_adc: gpio0_adc {
> + pins = "GPIO0";
> + function = "adc";
> + };
> +};
> +
> +&example_node {
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio0_adc>;
> +};
> +
> +GPIOs and their functions
> +-------------------------
> +
> +GPIO | Functions
> +------------------------
> +GPIO0 | gpio_in, gpio_out, ldo, adc
> +GPIO1 | gpio_in, gpio_out, ldo, adc
> +GPIO2 | gpio_in, gpio_out
> diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c
> index 4a346b7b4172..96ef0cc28762 100644
> --- a/drivers/pinctrl/pinctrl-axp209.c
> +++ b/drivers/pinctrl/pinctrl-axp209.c
> @@ -1,7 +1,8 @@
> /*
> - * AXP20x GPIO driver
> + * AXP20x pinctrl and GPIO driver
> *
> * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
> + * Copyright (C) 2017 Quentin Schulz <quentin.schulz@free-electrons.com>
> *
> * This program is free software; you can redistribute it and/or modify it
> * under the terms of the GNU General Public License as published by the
> @@ -22,14 +23,57 @@
> #include <linux/regmap.h>
> #include <linux/slab.h>
>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +
You should order the headers by alphabetical order.
> #define AXP20X_GPIO_FUNCTIONS 0x7
> #define AXP20X_GPIO_FUNCTION_OUT_LOW 0
> #define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
> #define AXP20X_GPIO_FUNCTION_INPUT 2
>
> +#define AXP20X_FUNC_GPIO_OUT 0
> +#define AXP20X_FUNC_GPIO_IN 1
> +#define AXP20X_FUNC_LDO 2
> +#define AXP20X_FUNC_ADC 3
> +#define AXP20X_FUNCS_NB 4
> +
> +struct axp20x_pctrl_desc {
> + const struct pinctrl_pin_desc *pins;
> + unsigned int npins;
> + /* Stores the pins supporting LDO function. Bit offset is pin number. */
> + unsigned int ldo_mask;
> + /* Stores the pins supporting ADC function. Bit offset is pin number. */
> + unsigned int adc_mask;
You can use u8 here.
> +};
> +
> +struct axp20x_pinctrl_function {
> + const char *name;
> + unsigned int muxval;
> + const char **groups;
> + unsigned int ngroups;
> +};
> +
> struct axp20x_gpio {
> struct gpio_chip chip;
> struct regmap *regmap;
> + struct pinctrl_dev *pctl_dev;
> + struct device *dev;
> + const struct axp20x_pctrl_desc *desc;
> + struct axp20x_pinctrl_function funcs[AXP20X_FUNCS_NB];
> +};
> +
> +static const struct pinctrl_pin_desc axp209_pins[] = {
> + PINCTRL_PIN(0, "GPIO0"),
> + PINCTRL_PIN(1, "GPIO1"),
> + PINCTRL_PIN(2, "GPIO2"),
> +};
> +
> +static const struct axp20x_pctrl_desc axp20x_data = {
> + .pins = axp209_pins,
> + .npins = ARRAY_SIZE(axp209_pins),
> + .ldo_mask = BIT(0) | BIT(1),
> + .adc_mask = BIT(0) | BIT(1),
> };
>
> static int axp20x_gpio_get_reg(unsigned offset)
> @@ -48,16 +92,7 @@ static int axp20x_gpio_get_reg(unsigned offset)
>
> static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
> {
> - struct axp20x_gpio *gpio = gpiochip_get_data(chip);
> - int reg;
> -
> - reg = axp20x_gpio_get_reg(offset);
> - if (reg < 0)
> - return reg;
> -
> - return regmap_update_bits(gpio->regmap, reg,
> - AXP20X_GPIO_FUNCTIONS,
> - AXP20X_GPIO_FUNCTION_INPUT);
> + return pinctrl_gpio_direction_input(chip->base + offset);
> }
>
> static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
> @@ -105,29 +140,210 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
> static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
> int value)
> {
> + chip->set(chip, offset, value);
> +
> + return 0;
> +}
> +
> +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
> + int value)
> +{
> struct axp20x_gpio *gpio = gpiochip_get_data(chip);
> int reg;
>
> reg = axp20x_gpio_get_reg(offset);
> if (reg < 0)
> + return;
> +
> + regmap_update_bits(gpio->regmap, reg,
> + AXP20X_GPIO_FUNCTIONS,
> + value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :
> + AXP20X_GPIO_FUNCTION_OUT_LOW);
> +}
> +
> +static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset,
> + u8 config)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> + int reg;
> +
> + reg = axp20x_gpio_get_reg(offset);
> + if (reg < 0)
> return reg;
>
> - return regmap_update_bits(gpio->regmap, reg,
> - AXP20X_GPIO_FUNCTIONS,
> - value ? AXP20X_GPIO_FUNCTION_OUT_HIGH
> - : AXP20X_GPIO_FUNCTION_OUT_LOW);
> + return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS,
> + config);
> }
>
> -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
> - int value)
> +static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return ARRAY_SIZE(gpio->funcs);
> +}
> +
> +static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> {
> - axp20x_gpio_output(chip, offset, value);
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->funcs[selector].name;
> +}
> +
> +static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const char * const **groups,
> + unsigned int *num_groups)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + *groups = gpio->funcs[selector].groups;
> + *num_groups = gpio->funcs[selector].ngroups;
> +
> + return 0;
> +}
> +
> +static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int function, unsigned int group)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int mask;
> +
> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
> + if (function <= AXP20X_FUNC_GPIO_IN)
> + return axp20x_pmx_set(pctldev, group,
> + gpio->funcs[function].muxval);
> +
> + if (function == AXP20X_FUNC_LDO)
> + mask = gpio->desc->ldo_mask;
> + else
> + mask = gpio->desc->adc_mask;
> +
> + if (!(BIT(group) & mask))
> + return -EINVAL;
> +
> + return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval);
> +}
> +
> +static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int offset, bool input)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + if (input)
> + return axp20x_pmx_set(pctldev, offset,
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval);
> +
> + return axp20x_pmx_set(pctldev, offset,
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval);
> +}
> +
> +static const struct pinmux_ops axp20x_pmx_ops = {
> + .get_functions_count = axp20x_pmx_func_cnt,
> + .get_function_name = axp20x_pmx_func_name,
> + .get_function_groups = axp20x_pmx_func_groups,
> + .set_mux = axp20x_pmx_set_mux,
> + .gpio_set_direction = axp20x_pmx_gpio_set_direction,
> + .strict = true,
> +};
> +
> +static int axp20x_groups_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->desc->npins;
> +}
> +
> +static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
> + const unsigned int **pins, unsigned int *num_pins)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + *pins = (unsigned int *)&gpio->desc->pins[selector];
> + *num_pins = 1;
> +
> + return 0;
> +}
> +
> +static const char *axp20x_group_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->desc->pins[selector].name;
> +}
> +
> +static const struct pinctrl_ops axp20x_pctrl_ops = {
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> + .dt_free_map = pinconf_generic_dt_free_map,
> + .get_groups_count = axp20x_groups_cnt,
> + .get_group_name = axp20x_group_name,
> + .get_group_pins = axp20x_group_pins,
> +};
> +
> +static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask,
> + unsigned int mask_len,
> + struct axp20x_pinctrl_function *func,
> + const struct pinctrl_pin_desc *pins)
> +{
> + unsigned long int mask_cpy = mask;
> + const char **group;
> + unsigned int ngroups = hweight8(mask);
> + int bit;
> +
> + func->ngroups = ngroups;
> + if (func->ngroups > 0) {
> + func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *),
> + GFP_KERNEL);
> + group = func->groups;
> + for_each_set_bit(bit, &mask_cpy, mask_len) {
> + *group = pins[bit].name;
> + group++;
> + }
> + }
> +}
> +
> +static void axp20x_build_funcs_groups(struct platform_device *pdev)
> +{
> + struct axp20x_gpio *gpio = platform_get_drvdata(pdev);
> + int i, pin;
> +
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out";
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = 0x0;
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in";
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = 0x2;
Why do you need hexadecimal values here?
> + gpio->funcs[AXP20X_FUNC_LDO].name = "ldo";
> + gpio->funcs[AXP20X_FUNC_LDO].muxval = 0x3;
> + gpio->funcs[AXP20X_FUNC_ADC].name = "adc";
> + gpio->funcs[AXP20X_FUNC_ADC].muxval = 0x4;
And you probably can define that statically.
> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
> + for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) {
> + gpio->funcs[i].ngroups = gpio->desc->npins;
> + gpio->funcs[i].groups = devm_kzalloc(&pdev->dev,
> + gpio->desc->npins * sizeof(const char *),
> + GFP_KERNEL);
> + for (pin = 0; pin < gpio->desc->npins; pin++)
> + gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name;
> + }
> +
> + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask,
> + gpio->desc->npins,
> + &gpio->funcs[AXP20X_FUNC_LDO],
> + gpio->desc->pins);
> +
> + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask,
> + gpio->desc->npins,
> + &gpio->funcs[AXP20X_FUNC_ADC],
> + gpio->desc->pins);
> }
>
> static int axp20x_gpio_probe(struct platform_device *pdev)
> {
> struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> struct axp20x_gpio *gpio;
> + struct pinctrl_desc *pctrl_desc;
> int ret;
>
> if (!of_device_is_available(pdev->dev.of_node))
> @@ -144,6 +360,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
>
> gpio->chip.base = -1;
> gpio->chip.can_sleep = true;
> + gpio->chip.request = gpiochip_generic_request;
> + gpio->chip.free = gpiochip_generic_free;
> gpio->chip.parent = &pdev->dev;
> gpio->chip.label = dev_name(&pdev->dev);
> gpio->chip.owner = THIS_MODULE;
> @@ -154,15 +372,49 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
> gpio->chip.direction_output = axp20x_gpio_output;
> gpio->chip.ngpio = 3;
>
> + gpio->desc = &axp20x_data;
> +
> gpio->regmap = axp20x->regmap;
>
> + gpio->dev = &pdev->dev;
> +
Why do you need new lines between each affectation?
> + platform_set_drvdata(pdev, gpio);
> +
> + axp20x_build_funcs_groups(pdev);
> +
> + pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);
> + if (!pctrl_desc)
> + return -ENOMEM;
> +
> + pctrl_desc->name = dev_name(&pdev->dev);
> + pctrl_desc->owner = THIS_MODULE;
> + pctrl_desc->pins = gpio->desc->pins;
> + pctrl_desc->npins = gpio->desc->npins;
> + pctrl_desc->pctlops = &axp20x_pctrl_ops;
> + pctrl_desc->pmxops = &axp20x_pmx_ops;
> +
> + gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio);
> + if (IS_ERR(gpio->pctl_dev)) {
> + dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
> + return PTR_ERR(gpio->pctl_dev);
> + }
> +
> ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
> if (ret) {
> dev_err(&pdev->dev, "Failed to register GPIO chip\n");
> return ret;
> }
>
> - dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n");
> + ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev),
> + gpio->desc->pins->number,
> + gpio->desc->pins->number,
> + gpio->desc->npins);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to add pin range\n");
> + return ret;
> + }
> +
> + dev_info(&pdev->dev, "AXP209 pinctrl and GPIO driver loaded\n");
>
> return 0;
> }
> @@ -184,5 +436,6 @@ static struct platform_driver axp20x_gpio_driver = {
> module_platform_driver(axp20x_gpio_driver);
>
> MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> -MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_DESCRIPTION("AXP20x PMIC pinctrl and GPIO driver");
> MODULE_LICENSE("GPL");
Looks much better otherwise, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171002/6e756ad4/attachment.sig>
WARNING: multiple messages have this Message-ID (diff)
From: Maxime Ripard <maxime.ripard@free-electrons.com>
To: Quentin Schulz <quentin.schulz@free-electrons.com>
Cc: linus.walleij@linaro.org, robh+dt@kernel.org,
mark.rutland@arm.com, wens@csie.org, linux@armlinux.org.uk,
lee.jones@linaro.org, linux-gpio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
thomas.petazzoni@free-electrons.com,
linux-sunxi@googlegroups.com
Subject: Re: [PATCH v3 02/12] pinctrl: axp209: add pinctrl features
Date: Mon, 2 Oct 2017 22:37:44 +0200 [thread overview]
Message-ID: <20171002203744.e2hhrntjclydvkpg@flea> (raw)
In-Reply-To: <20171002120854.5212-3-quentin.schulz@free-electrons.com>
[-- Attachment #1: Type: text/plain, Size: 14504 bytes --]
On Mon, Oct 02, 2017 at 12:08:44PM +0000, Quentin Schulz wrote:
> The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
> an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
>
> This adds the pinctrl features to the driver so GPIO0/1 can be used as
> ADC or LDO regulator.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> .../devicetree/bindings/pinctrl/pinctrl-axp209.txt | 28 +-
> drivers/pinctrl/pinctrl-axp209.c | 293 +++++++++++++++++++--
> 2 files changed, 300 insertions(+), 21 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> index a6611304dd3c..388c04492afd 100644
> --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-axp209.txt
> @@ -1,4 +1,4 @@
> -AXP209 GPIO controller
> +AXP209 GPIO & pinctrl controller
>
> This driver follows the usual GPIO bindings found in
> Documentation/devicetree/bindings/gpio/gpio.txt
> @@ -28,3 +28,29 @@ axp209: pmic@34 {
> #gpio-cells = <2>;
> };
> };
> +
> +The GPIOs can be muxed to other functions and therefore, must be a subnode of
> +axp_gpio.
> +
> +Example:
> +
> +&axp_gpio {
> + gpio0_adc: gpio0_adc {
> + pins = "GPIO0";
> + function = "adc";
> + };
> +};
> +
> +&example_node {
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio0_adc>;
> +};
> +
> +GPIOs and their functions
> +-------------------------
> +
> +GPIO | Functions
> +------------------------
> +GPIO0 | gpio_in, gpio_out, ldo, adc
> +GPIO1 | gpio_in, gpio_out, ldo, adc
> +GPIO2 | gpio_in, gpio_out
> diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c
> index 4a346b7b4172..96ef0cc28762 100644
> --- a/drivers/pinctrl/pinctrl-axp209.c
> +++ b/drivers/pinctrl/pinctrl-axp209.c
> @@ -1,7 +1,8 @@
> /*
> - * AXP20x GPIO driver
> + * AXP20x pinctrl and GPIO driver
> *
> * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
> + * Copyright (C) 2017 Quentin Schulz <quentin.schulz@free-electrons.com>
> *
> * This program is free software; you can redistribute it and/or modify it
> * under the terms of the GNU General Public License as published by the
> @@ -22,14 +23,57 @@
> #include <linux/regmap.h>
> #include <linux/slab.h>
>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +
You should order the headers by alphabetical order.
> #define AXP20X_GPIO_FUNCTIONS 0x7
> #define AXP20X_GPIO_FUNCTION_OUT_LOW 0
> #define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
> #define AXP20X_GPIO_FUNCTION_INPUT 2
>
> +#define AXP20X_FUNC_GPIO_OUT 0
> +#define AXP20X_FUNC_GPIO_IN 1
> +#define AXP20X_FUNC_LDO 2
> +#define AXP20X_FUNC_ADC 3
> +#define AXP20X_FUNCS_NB 4
> +
> +struct axp20x_pctrl_desc {
> + const struct pinctrl_pin_desc *pins;
> + unsigned int npins;
> + /* Stores the pins supporting LDO function. Bit offset is pin number. */
> + unsigned int ldo_mask;
> + /* Stores the pins supporting ADC function. Bit offset is pin number. */
> + unsigned int adc_mask;
You can use u8 here.
> +};
> +
> +struct axp20x_pinctrl_function {
> + const char *name;
> + unsigned int muxval;
> + const char **groups;
> + unsigned int ngroups;
> +};
> +
> struct axp20x_gpio {
> struct gpio_chip chip;
> struct regmap *regmap;
> + struct pinctrl_dev *pctl_dev;
> + struct device *dev;
> + const struct axp20x_pctrl_desc *desc;
> + struct axp20x_pinctrl_function funcs[AXP20X_FUNCS_NB];
> +};
> +
> +static const struct pinctrl_pin_desc axp209_pins[] = {
> + PINCTRL_PIN(0, "GPIO0"),
> + PINCTRL_PIN(1, "GPIO1"),
> + PINCTRL_PIN(2, "GPIO2"),
> +};
> +
> +static const struct axp20x_pctrl_desc axp20x_data = {
> + .pins = axp209_pins,
> + .npins = ARRAY_SIZE(axp209_pins),
> + .ldo_mask = BIT(0) | BIT(1),
> + .adc_mask = BIT(0) | BIT(1),
> };
>
> static int axp20x_gpio_get_reg(unsigned offset)
> @@ -48,16 +92,7 @@ static int axp20x_gpio_get_reg(unsigned offset)
>
> static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset)
> {
> - struct axp20x_gpio *gpio = gpiochip_get_data(chip);
> - int reg;
> -
> - reg = axp20x_gpio_get_reg(offset);
> - if (reg < 0)
> - return reg;
> -
> - return regmap_update_bits(gpio->regmap, reg,
> - AXP20X_GPIO_FUNCTIONS,
> - AXP20X_GPIO_FUNCTION_INPUT);
> + return pinctrl_gpio_direction_input(chip->base + offset);
> }
>
> static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset)
> @@ -105,29 +140,210 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
> static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset,
> int value)
> {
> + chip->set(chip, offset, value);
> +
> + return 0;
> +}
> +
> +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
> + int value)
> +{
> struct axp20x_gpio *gpio = gpiochip_get_data(chip);
> int reg;
>
> reg = axp20x_gpio_get_reg(offset);
> if (reg < 0)
> + return;
> +
> + regmap_update_bits(gpio->regmap, reg,
> + AXP20X_GPIO_FUNCTIONS,
> + value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :
> + AXP20X_GPIO_FUNCTION_OUT_LOW);
> +}
> +
> +static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset,
> + u8 config)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> + int reg;
> +
> + reg = axp20x_gpio_get_reg(offset);
> + if (reg < 0)
> return reg;
>
> - return regmap_update_bits(gpio->regmap, reg,
> - AXP20X_GPIO_FUNCTIONS,
> - value ? AXP20X_GPIO_FUNCTION_OUT_HIGH
> - : AXP20X_GPIO_FUNCTION_OUT_LOW);
> + return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS,
> + config);
> }
>
> -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
> - int value)
> +static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return ARRAY_SIZE(gpio->funcs);
> +}
> +
> +static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> {
> - axp20x_gpio_output(chip, offset, value);
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->funcs[selector].name;
> +}
> +
> +static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const char * const **groups,
> + unsigned int *num_groups)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + *groups = gpio->funcs[selector].groups;
> + *num_groups = gpio->funcs[selector].ngroups;
> +
> + return 0;
> +}
> +
> +static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int function, unsigned int group)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int mask;
> +
> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
> + if (function <= AXP20X_FUNC_GPIO_IN)
> + return axp20x_pmx_set(pctldev, group,
> + gpio->funcs[function].muxval);
> +
> + if (function == AXP20X_FUNC_LDO)
> + mask = gpio->desc->ldo_mask;
> + else
> + mask = gpio->desc->adc_mask;
> +
> + if (!(BIT(group) & mask))
> + return -EINVAL;
> +
> + return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval);
> +}
> +
> +static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int offset, bool input)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + if (input)
> + return axp20x_pmx_set(pctldev, offset,
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval);
> +
> + return axp20x_pmx_set(pctldev, offset,
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval);
> +}
> +
> +static const struct pinmux_ops axp20x_pmx_ops = {
> + .get_functions_count = axp20x_pmx_func_cnt,
> + .get_function_name = axp20x_pmx_func_name,
> + .get_function_groups = axp20x_pmx_func_groups,
> + .set_mux = axp20x_pmx_set_mux,
> + .gpio_set_direction = axp20x_pmx_gpio_set_direction,
> + .strict = true,
> +};
> +
> +static int axp20x_groups_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->desc->npins;
> +}
> +
> +static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
> + const unsigned int **pins, unsigned int *num_pins)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + *pins = (unsigned int *)&gpio->desc->pins[selector];
> + *num_pins = 1;
> +
> + return 0;
> +}
> +
> +static const char *axp20x_group_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
> +
> + return gpio->desc->pins[selector].name;
> +}
> +
> +static const struct pinctrl_ops axp20x_pctrl_ops = {
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> + .dt_free_map = pinconf_generic_dt_free_map,
> + .get_groups_count = axp20x_groups_cnt,
> + .get_group_name = axp20x_group_name,
> + .get_group_pins = axp20x_group_pins,
> +};
> +
> +static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask,
> + unsigned int mask_len,
> + struct axp20x_pinctrl_function *func,
> + const struct pinctrl_pin_desc *pins)
> +{
> + unsigned long int mask_cpy = mask;
> + const char **group;
> + unsigned int ngroups = hweight8(mask);
> + int bit;
> +
> + func->ngroups = ngroups;
> + if (func->ngroups > 0) {
> + func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *),
> + GFP_KERNEL);
> + group = func->groups;
> + for_each_set_bit(bit, &mask_cpy, mask_len) {
> + *group = pins[bit].name;
> + group++;
> + }
> + }
> +}
> +
> +static void axp20x_build_funcs_groups(struct platform_device *pdev)
> +{
> + struct axp20x_gpio *gpio = platform_get_drvdata(pdev);
> + int i, pin;
> +
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out";
> + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = 0x0;
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in";
> + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = 0x2;
Why do you need hexadecimal values here?
> + gpio->funcs[AXP20X_FUNC_LDO].name = "ldo";
> + gpio->funcs[AXP20X_FUNC_LDO].muxval = 0x3;
> + gpio->funcs[AXP20X_FUNC_ADC].name = "adc";
> + gpio->funcs[AXP20X_FUNC_ADC].muxval = 0x4;
And you probably can define that statically.
> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
> + for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) {
> + gpio->funcs[i].ngroups = gpio->desc->npins;
> + gpio->funcs[i].groups = devm_kzalloc(&pdev->dev,
> + gpio->desc->npins * sizeof(const char *),
> + GFP_KERNEL);
> + for (pin = 0; pin < gpio->desc->npins; pin++)
> + gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name;
> + }
> +
> + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask,
> + gpio->desc->npins,
> + &gpio->funcs[AXP20X_FUNC_LDO],
> + gpio->desc->pins);
> +
> + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask,
> + gpio->desc->npins,
> + &gpio->funcs[AXP20X_FUNC_ADC],
> + gpio->desc->pins);
> }
>
> static int axp20x_gpio_probe(struct platform_device *pdev)
> {
> struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> struct axp20x_gpio *gpio;
> + struct pinctrl_desc *pctrl_desc;
> int ret;
>
> if (!of_device_is_available(pdev->dev.of_node))
> @@ -144,6 +360,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
>
> gpio->chip.base = -1;
> gpio->chip.can_sleep = true;
> + gpio->chip.request = gpiochip_generic_request;
> + gpio->chip.free = gpiochip_generic_free;
> gpio->chip.parent = &pdev->dev;
> gpio->chip.label = dev_name(&pdev->dev);
> gpio->chip.owner = THIS_MODULE;
> @@ -154,15 +372,49 @@ static int axp20x_gpio_probe(struct platform_device *pdev)
> gpio->chip.direction_output = axp20x_gpio_output;
> gpio->chip.ngpio = 3;
>
> + gpio->desc = &axp20x_data;
> +
> gpio->regmap = axp20x->regmap;
>
> + gpio->dev = &pdev->dev;
> +
Why do you need new lines between each affectation?
> + platform_set_drvdata(pdev, gpio);
> +
> + axp20x_build_funcs_groups(pdev);
> +
> + pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);
> + if (!pctrl_desc)
> + return -ENOMEM;
> +
> + pctrl_desc->name = dev_name(&pdev->dev);
> + pctrl_desc->owner = THIS_MODULE;
> + pctrl_desc->pins = gpio->desc->pins;
> + pctrl_desc->npins = gpio->desc->npins;
> + pctrl_desc->pctlops = &axp20x_pctrl_ops;
> + pctrl_desc->pmxops = &axp20x_pmx_ops;
> +
> + gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio);
> + if (IS_ERR(gpio->pctl_dev)) {
> + dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
> + return PTR_ERR(gpio->pctl_dev);
> + }
> +
> ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
> if (ret) {
> dev_err(&pdev->dev, "Failed to register GPIO chip\n");
> return ret;
> }
>
> - dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n");
> + ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev),
> + gpio->desc->pins->number,
> + gpio->desc->pins->number,
> + gpio->desc->npins);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to add pin range\n");
> + return ret;
> + }
> +
> + dev_info(&pdev->dev, "AXP209 pinctrl and GPIO driver loaded\n");
>
> return 0;
> }
> @@ -184,5 +436,6 @@ static struct platform_driver axp20x_gpio_driver = {
> module_platform_driver(axp20x_gpio_driver);
>
> MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> -MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_DESCRIPTION("AXP20x PMIC pinctrl and GPIO driver");
> MODULE_LICENSE("GPL");
Looks much better otherwise, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
next prev parent reply other threads:[~2017-10-02 20:37 UTC|newest]
Thread overview: 107+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-02 12:08 [PATCH v3 00/12] add pinmuxing support for pins in AXP209 and AXP813 PMICs Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-1-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 12:08 ` [PATCH v3 01/12] pinctrl: move gpio-axp209 to pinctrl Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-2-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 20:18 ` Maxime Ripard
2017-10-02 20:18 ` Maxime Ripard
2017-10-02 20:18 ` Maxime Ripard
2017-10-03 9:01 ` Chen-Yu Tsai
2017-10-03 9:01 ` Chen-Yu Tsai
2017-10-03 9:01 ` Chen-Yu Tsai
2017-10-02 12:08 ` [PATCH v3 02/12] pinctrl: axp209: add pinctrl features Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-3-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 20:37 ` Maxime Ripard [this message]
2017-10-02 20:37 ` Maxime Ripard
2017-10-02 20:37 ` Maxime Ripard
2017-10-10 18:15 ` Rob Herring
2017-10-10 18:15 ` Rob Herring
2017-10-10 18:15 ` Rob Herring
2017-10-02 12:08 ` [PATCH v3 03/12] pinctrl: axp209: rename everything from gpio to pctl Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` [PATCH v3 04/12] pinctrl: axp209: add programmable gpio_status_offset Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-5-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 20:38 ` Maxime Ripard
2017-10-02 20:38 ` Maxime Ripard
2017-10-02 20:38 ` Maxime Ripard
2017-10-03 9:01 ` Chen-Yu Tsai
2017-10-03 9:01 ` Chen-Yu Tsai
2017-10-03 9:01 ` Chen-Yu Tsai
2017-10-02 12:08 ` [PATCH v3 05/12] pinctrl: axp209: add support for AXP813 GPIOs Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-6-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 20:38 ` Maxime Ripard
2017-10-02 20:38 ` Maxime Ripard
2017-10-02 20:38 ` Maxime Ripard
2017-10-10 18:33 ` Rob Herring
2017-10-10 18:33 ` Rob Herring
2017-10-10 18:33 ` Rob Herring
2017-10-02 12:08 ` [PATCH v3 06/12] mfd: axp20x: add pinctrl cell for AXP813 Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-7-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 20:39 ` Maxime Ripard
2017-10-02 20:39 ` Maxime Ripard
2017-10-02 20:39 ` Maxime Ripard
2017-10-02 12:08 ` [PATCH v3 07/12] ARM: dts: sun8i: a711: include axp81x dtsi Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
[not found] ` <20171002120854.5212-8-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-02 20:40 ` Maxime Ripard
2017-10-02 20:40 ` Maxime Ripard
2017-10-02 20:40 ` Maxime Ripard
2017-10-02 12:08 ` [PATCH v3 08/12] ARM: dts: sun8i: bananapi-m3: " Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` [PATCH v3 09/12] ARM: dts: sun8i: h8homlet-v2: " Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` [PATCH v3 10/12] ARM: dts: sun8i: cubietruck-plus: " Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` [PATCH v3 11/12] ARM: dtsi: axp81x: add GPIO DT node Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` [PATCH v3 12/12] ARM: dtsi: axp81x: set pinmux for GPIO0/1 when used as LDOs Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 12:08 ` Quentin Schulz
2017-10-02 20:42 ` Maxime Ripard
2017-10-02 20:42 ` Maxime Ripard
2017-10-03 2:06 ` Chen-Yu Tsai
2017-10-03 2:06 ` Chen-Yu Tsai
2017-10-03 2:06 ` Chen-Yu Tsai
2017-10-03 9:18 ` Russell King - ARM Linux
2017-10-03 9:18 ` Russell King - ARM Linux
[not found] ` <20171003091837.GK20805-l+eeeJia6m9URfEZ8mYm6t73F7V6hmMc@public.gmane.org>
2017-10-03 14:43 ` Maxime Ripard
2017-10-03 14:43 ` Maxime Ripard
2017-10-03 14:43 ` Maxime Ripard
[not found] ` <20171002120854.5212-13-quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-03 9:27 ` Linus Walleij
2017-10-03 9:27 ` Linus Walleij
2017-10-03 9:27 ` Linus Walleij
[not found] ` <CACRpkdZYcSuhvn0b0qa0k1PQHoyXjNyjoyhR2sPme_t1pF12Dg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-10-03 14:47 ` Maxime Ripard
2017-10-03 14:47 ` Maxime Ripard
2017-10-03 14:47 ` Maxime Ripard
2017-10-03 15:08 ` Chen-Yu Tsai
2017-10-03 15:08 ` Chen-Yu Tsai
2017-10-03 15:08 ` Chen-Yu Tsai
[not found] ` <CAGb2v64=XL34x7iGJcEmmVnmzkA5wmyZJd-g827bY=kdHcZOMA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-10-04 7:35 ` Quentin Schulz
2017-10-04 7:35 ` Quentin Schulz
2017-10-04 7:35 ` Quentin Schulz
[not found] ` <8be22e47-8071-be4c-0bd4-38fa51cbe3ff-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-10 3:09 ` Chen-Yu Tsai
2017-10-10 3:09 ` Chen-Yu Tsai
2017-10-10 3:09 ` Chen-Yu Tsai
[not found] ` <CAGb2v65-_MjUsfTHMoyzvczs_280p7NXEsStSaoR6wnvTCq5+A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-10-11 12:00 ` Maxime Ripard
2017-10-11 12:00 ` Maxime Ripard
2017-10-11 12:00 ` Maxime Ripard
2017-10-11 19:09 ` Linus Walleij
2017-10-11 19:09 ` Linus Walleij
[not found] ` <CACRpkdY0GWqjkbxm=OBfRsTmkQ7kc86Dw96XUT08azSbD57Xrw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-10-12 2:22 ` Chen-Yu Tsai
2017-10-12 2:22 ` Chen-Yu Tsai
2017-10-12 2:22 ` Chen-Yu Tsai
2017-10-11 7:43 ` Linus Walleij
2017-10-11 7:43 ` Linus Walleij
2017-10-11 7:43 ` Linus Walleij
2017-10-07 10:48 ` [PATCH v3 00/12] add pinmuxing support for pins in AXP209 and AXP813 PMICs Linus Walleij
2017-10-07 10:48 ` Linus Walleij
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171002203744.e2hhrntjclydvkpg@flea \
--to=maxime.ripard-wi1+55scjutkeb57/3fjtnbpr1lh4cv8@public.gmane.org \
--cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
--cc=linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
--cc=linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org \
--cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
--cc=linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org \
--cc=mark.rutland-5wv7dgnIgG8@public.gmane.org \
--cc=quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org \
--cc=robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
--cc=thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org \
--cc=wens-jdAy2FN1RRM@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.