lkml.org 
[lkml]   [2022]   [Jun]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
From
Date
SubjectRe: [PATCH v3 13/16] pinctrl: Add AXP192 pin control driver
On Sat, Jun 18, 2022 at 11:40 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> wrote:
>
> The AXP192 PMIC's GPIO registers are much different from the GPIO
> registers of the AXP20x and AXP813 PMICs supported by the existing
> pinctrl-axp209 driver. It makes more sense to add a new driver for
> the AXP192, rather than add support in the existing axp20x driver.
>
> The pinctrl-axp192 driver is considerably more flexible in terms of
> register layout and should be able to support other X-Powers PMICs.
> Interrupts and pull down resistor configuration are supported too.

...

> +config PINCTRL_AXP192
> + tristate "X-Powers AXP192 PMIC pinctrl and GPIO Support"
> + depends on MFD_AXP20X


> + depends on OF

Why?

> + select PINMUX
> + select GENERIC_PINCONF
> + select GPIOLIB

...

> +#include <linux/of.h>
> +#include <linux/of_device.h>

Why?

...

> +struct axp192_pctl_function {
> + const char *name;
> + /* Mux value written to the control register to select the function (-1 if unsupported) */

Comment is misleading. -1 can't be a value of unsigned type.

> + const u8 *muxvals;
> + const char * const *groups;
> + unsigned int ngroups;
> +};

...

> +struct axp192_pctl_desc {
> + unsigned int npins;
> + const struct pinctrl_pin_desc *pins;
> + /* Description of the function control register for each pin */
> + const struct axp192_pctl_reg_info *ctrl_regs;
> + /* Description of the output signal register for each pin */
> + const struct axp192_pctl_reg_info *out_regs;
> + /* Description of the input signal register for each pin */
> + const struct axp192_pctl_reg_info *in_regs;
> + /* Description of the pull down resistor config register for each pin */

Can you just convert these comments to a kernel-doc?

> + const struct axp192_pctl_reg_info *pull_down_regs;
> +
> + unsigned int nfunctions;
> + const struct axp192_pctl_function *functions;
> +};

...

> +
> +

One blank line is enough.

...

> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + ret = axp192_pinconf_get_pull_down(pctldev, pin);
> + if (ret < 0)
> + return ret;

> + else if (ret != 0)

1. Redundant 'else'
2. if (ret > 0)

> + return -EINVAL;
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + ret = axp192_pinconf_get_pull_down(pctldev, pin);
> + if (ret < 0)
> + return ret;
> + else if (ret == 0)

Ditto.

Looking at this I would rather expect the function to return something
defined, than 0, non-0.

> + return -EINVAL;
> + break;

> + default:
> + return -ENOTSUPP;
> + }

...

> + for (cfg = 0; cfg < num_configs; ++cfg) {

cfg++ will work the same way and easier to read.

> + switch (pinconf_to_config_param(configs[cfg])) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + ret = axp192_pinconf_set_pull_down(pctldev, pin, 0);
> + if (ret)
> + return ret;
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + ret = axp192_pinconf_set_pull_down(pctldev, pin, 1);
> + if (ret)
> + return ret;
> + break;
> +
> + default:
> + return -ENOTSUPP;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct pinconf_ops axp192_conf_ops = {
> + .is_generic = true,
> + .pin_config_get = axp192_pinconf_get,
> + .pin_config_set = axp192_pinconf_set,
> + .pin_config_group_get = axp192_pinconf_get,
> + .pin_config_group_set = axp192_pinconf_set,
> +};
> +
> +static int axp192_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset, u8 config)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> + const struct axp192_pctl_reg_info *reginfo = &pctl->desc->ctrl_regs[offset];
> + unsigned int regval = config << (ffs(reginfo->mask) - 1);
> +
> + return regmap_update_bits(pctl->regmap, reginfo->reg, reginfo->mask, regval);
> +}
> +
> +static int axp192_pmx_func_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctl->desc->nfunctions;
> +}
> +
> +static const char *axp192_pmx_func_name(struct pinctrl_dev *pctldev, unsigned int selector)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctl->desc->functions[selector].name;
> +}
> +
> +static int axp192_pmx_func_groups(struct pinctrl_dev *pctldev, unsigned int selector,
> + const char * const **groups, unsigned int *num_groups)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +
> + *groups = pctl->desc->functions[selector].groups;
> + *num_groups = pctl->desc->functions[selector].ngroups;
> +
> + return 0;
> +}
> +
> +static int axp192_pmx_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int function, unsigned int group)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> + const u8 *muxvals = pctl->desc->functions[function].muxvals;
> +
> + if (muxvals[group] == U8_MAX)
> + return -EINVAL;
> +
> + /*
> + * Switching to LDO or PWM function will enable LDO/PWM output, so it's
> + * better to ignore these requests and let the regulator or PWM drivers
> + * handle muxing to avoid interfering with them.
> + */
> + if (function == AXP192_FUNC_LDO || function == AXP192_FUNC_PWM)
> + return 0;
> +
> + return axp192_pmx_set(pctldev, group, muxvals[group]);
> +}
> +
> +static int axp192_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int offset, bool input)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> + const u8 *muxvals = input ? pctl->desc->functions[AXP192_FUNC_INPUT].muxvals
> + : pctl->desc->functions[AXP192_FUNC_OUTPUT].muxvals;
> +
> + return axp192_pmx_set(pctldev, offset, muxvals[offset]);
> +}
> +
> +static const struct pinmux_ops axp192_pmx_ops = {
> + .get_functions_count = axp192_pmx_func_cnt,
> + .get_function_name = axp192_pmx_func_name,
> + .get_function_groups = axp192_pmx_func_groups,
> + .set_mux = axp192_pmx_set_mux,
> + .gpio_set_direction = axp192_pmx_gpio_set_direction,
> + .strict = true,
> +};
> +
> +static int axp192_groups_cnt(struct pinctrl_dev *pctldev)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctl->desc->npins;
> +}
> +
> +static const char *axp192_group_name(struct pinctrl_dev *pctldev, unsigned int selector)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctl->desc->pins[selector].name;
> +}
> +
> +static int axp192_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
> + const unsigned int **pins, unsigned int *num_pins)
> +{
> + struct axp192_pctl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +
> + *pins = &pctl->desc->pins[selector].number;
> + *num_pins = 1;
> +
> + return 0;
> +}
> +
> +static const struct pinctrl_ops axp192_pctrl_ops = {
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> + .dt_free_map = pinconf_generic_dt_free_map,
> + .get_groups_count = axp192_groups_cnt,
> + .get_group_name = axp192_group_name,
> + .get_group_pins = axp192_group_pins,
> +};
> +
> +static int axp192_pctl_probe(struct platform_device *pdev)
> +{
> + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> + struct axp192_pctl *pctl;
> + struct pinctrl_desc *pctrl_desc;
> + int ret, i;
> +
> + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
> + if (!pctl)
> + return -ENOMEM;
> +
> + pctl->desc = device_get_match_data(&pdev->dev);
> + pctl->regmap = axp20x->regmap;
> + pctl->regmap_irqc = axp20x->regmap_irqc;
> + pctl->dev = &pdev->dev;
> +
> + pctl->chip.base = -1;
> + pctl->chip.can_sleep = true;
> + pctl->chip.request = gpiochip_generic_request;
> + pctl->chip.free = gpiochip_generic_free;
> + pctl->chip.parent = &pdev->dev;
> + pctl->chip.label = dev_name(&pdev->dev);
> + pctl->chip.owner = THIS_MODULE;
> + pctl->chip.get = axp192_gpio_get;
> + pctl->chip.get_direction = axp192_gpio_get_direction;
> + pctl->chip.set = axp192_gpio_set;
> + pctl->chip.direction_input = axp192_gpio_direction_input;
> + pctl->chip.direction_output = axp192_gpio_direction_output;
> + pctl->chip.to_irq = axp192_gpio_to_irq;
> + pctl->chip.ngpio = pctl->desc->npins;
> +
> + pctl->irqs = devm_kcalloc(&pdev->dev, pctl->desc->npins, sizeof(int), GFP_KERNEL);
> + if (!pctl->irqs)
> + return -ENOMEM;
> +
> + for (i = 0; i < pctl->desc->npins; ++i) {
> + ret = platform_get_irq_byname_optional(pdev, pctl->desc->pins[i].name);
> + if (ret > 0)
> + pctl->irqs[i] = ret;
> + }
> +
> + platform_set_drvdata(pdev, pctl);
> +
> + 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 = pctl->desc->pins;
> + pctrl_desc->npins = pctl->desc->npins;
> + pctrl_desc->pctlops = &axp192_pctrl_ops;
> + pctrl_desc->pmxops = &axp192_pmx_ops;
> + pctrl_desc->confops = &axp192_conf_ops;
> +
> + pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, pctl);
> + if (IS_ERR(pctl->pctl_dev))
> + dev_err_probe(&pdev->dev, PTR_ERR(pctl->pctl_dev),
> + "couldn't register pinctrl driver\n");
> +
> + ret = devm_gpiochip_add_data(&pdev->dev, &pctl->chip, pctl);
> + if (ret)
> + dev_err_probe(&pdev->dev, ret, "Failed to register GPIO chip\n");
> +
> + return 0;
> +}
> +
> +static const struct of_device_id axp192_pctl_match[] = {
> + { .compatible = "x-powers,axp192-gpio", .data = &axp192_data, },
> + { }
> +};

--
With Best Regards,
Andy Shevchenko

\
 
 \ /
  Last update: 2022-06-19 13:22    [W:0.277 / U:0.072 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site