Re: [PATCH 1/7] pinctrl: realtek: Add common pinctrl driver for Realtek DHC RTD SoCs
From: <hidden>
Date: 2023-08-14 21:30:18
Also in:
linux-gpio, lkml
Wed, Jul 26, 2023 at 05:04:03PM +0800, TY Chang kirjoitti:
The RTD SoCs share a similar design for pinmux and pinconfig. This common pinctrl driver supports different variants within the RTD SoCs.
+config PINCTRL_RTD + tristate "Realtek DHC core pin controller driver"
Why is it user-visible?
+ depends on ARCH_REALTEK
+ default y
Why?
+ select PINMUX + select GENERIC_PINCONF
...
+#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/module.h>
+#include <linux/of.h> +#include <linux/of_address.h>
Not used or not needed, use proper platform driver APIs
+#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h>
Move this group outside generic headers...
+#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h>
+ Blank line. You have missing headers (like types.h)/ ...here.
+#include "../core.h" +#include "../pinctrl-utils.h"
+ Blank line.
+#include "pinctrl-rtd.h"
...
+static const struct pinconf_generic_params rtd_custom_bindings[] = {
+ {"realtek,pdrive", RTD_P_DRIVE, 0},
+ {"realtek,ndrive", RTD_N_DRIVE, 0},
+ {"realtek,dcycle", RTD_D_CYCLE, 0},Use C99 initializers.
+};
...
+static void rtd_pinctrl_dbg_show(struct pinctrl_dev *pcdev,
+ struct seq_file *s,
+ unsigned int offset)
+{
+ struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
+ const struct rtd_pin_desc *mux = &data->info->muxes[offset];
+ const struct rtd_pin_mux_desc *func;
+ u32 val;
+ u32 mask;
+ u32 pin_val;+ int is_map;
Actually it's boolean. Why int?
+ if (!mux->name) {
+ seq_puts(s, "[not defined]");
+ return;
+ }
+ val = readl_relaxed(data->base + mux->mux_offset);
+ mask = mux->mux_mask;
+ pin_val = val & mask;
+
+ is_map = 0;
+ func = &mux->functions[0];
+ seq_puts(s, "function: ");+ while (func->name) {
+ if (func->mux_value == pin_val) {
+ is_map = 1;
+ seq_printf(s, "[%s] ", func->name);
+ } else {
+ seq_printf(s, "%s ", func->name);
+ }
+ func++;
+ }This can be a long list, why not simply print only mapped function?
+ if (!is_map) + seq_puts(s, "[not defined]"); +}
...
+static const struct rtd_pin_desc *rtd_pinctrl_find_mux(struct rtd_pinctrl *data, unsigned int pin)
+{
+ if (!data->info->muxes[pin].name)
+ return &data->info->muxes[pin];
+
+ return NULL;Can the error (?) case be handled first? if (data->info->muxes[pin].name) return NULL;
+}
...
+static void rtd_pinctrl_update_bits(struct rtd_pinctrl *data, unsigned int offset,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int reg = readl_relaxed(data->base + offset);+ reg &= ~mask; + reg |= (mask & val);
Please, use idiomatic one liner: reg = (reg & ~mask) | (val & mask);
+ writel_relaxed(reg, data->base + offset); +}
...
+static const struct pinctrl_pin_desc + *rtd_pinctrl_get_pin_by_number(struct rtd_pinctrl *data, int number)
Weird indentation.
+{
+ int i;
+
+ for (i = 0; i < data->info->num_pins; i++) {
+ if (data->info->pins[i].number == number)
+ return &data->info->pins[i];
+ }
+
+ return NULL;
+}...
+static const struct rtd_pin_config_desc
+ *rtd_pinctrl_find_config(struct rtd_pinctrl *data, unsigned int pin)
+{
+ if (!data->info->configs[pin].name)
+ return &data->info->configs[pin];
+
+ return NULL;if (...) return NULL;
+}
...
+ switch ((u32)param) {Why casting?!
+ case PIN_CONFIG_INPUT_SCHMITT:
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ if (config_desc->smt_offset == NA) {+ dev_err(data->dev, "Not support input schmitt for pin: %s\n", name);
In all cases like this, why do you need the message to be printed?
+ return -ENOTSUPP; + } + smt_off = config_desc->base_bit + config_desc->smt_offset; + set_val = arg; + + mask = BIT(smt_off); + val = set_val ? BIT(smt_off) : 0; + rtd_pinctrl_update_bits(data, config_desc->reg_offset, mask, val); + break;
...
+ case PIN_CONFIG_DRIVE_STRENGTH: + curr_off = config_desc->base_bit + config_desc->curr_offset; + strength = arg;
+ val = 0;
How is this assignment being used?
+ switch (config_desc->curr_type) {
+ case PADDRI_4_8:
+ if (strength == 4)BIT() ?
+ val = 0; + else if (strength == 8)
Ditto.
+ val = BIT(curr_off); + else + return -EINVAL; + break; + case PADDRI_2_4: + if (strength == 2)
Ditto.
+ val = 0; + else if (strength == 4)
Ditto.
+ val = BIT(curr_off);
+ else
+ return -EINVAL;
+ break;
+ case NA:
+ dev_err(data->dev, "Not support drive strength for pin: %s\n", name);
+ return -ENOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ mask = BIT(curr_off);
+ rtd_pinctrl_update_bits(data, config_desc->reg_offset, mask, val);
+ break;
+
+ case PIN_CONFIG_POWER_SOURCE:
+ if (config_desc->power_offset == NA) {
+ dev_err(data->dev, "Not support power source for pin: %s\n", name);
+ return -ENOTSUPP;
+ }
+ reg_off = config_desc->reg_offset;
+ pow_off = config_desc->base_bit + config_desc->power_offset;
+ if (pow_off >= 32) {
+ reg_off += 0x4;
+ pow_off -= 32;
+ }Less error prone is to use division and multiplication. Something like pow_bit = config_desc->base_bit + config_desc->power_offset; reg_off = config_desc->reg_offset + 4 * (pow_bit / 32); pow_off = pow_bit % 32;
+ set_val = arg;
+ mask = BIT(pow_off);
+ val = set_val ? mask : 0;
+ rtd_pinctrl_update_bits(data, reg_off, mask, val);
+ break;
+
+ case RTD_P_DRIVE:
+ sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr);
+ if (!sconfig_desc) {
+ dev_err(data->dev, "Not support P driving for pin: %s\n", name);
+ return -ENOTSUPP;
+ }
+ set_val = arg;
+ reg_off = sconfig_desc->reg_offset;
+ p_off = sconfig_desc->pdrive_offset;
+ if (p_off >= 32) {
+ reg_off += 0x4;
+ p_off -= 32;
+ }Ditto.
+ mask = GENMASK(p_off + sconfig_desc->pdrive_maskbits - 1, p_off);
No, this is suboptimal, better mask = GENMASK(sconfig_desc->pdrive_maskbits - 1, 0) << p_off;
+ val = set_val << p_off;
+ rtd_pinctrl_update_bits(data, reg_off, mask, val);
+ break;
+
+ case RTD_N_DRIVE:
+ sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr);
+ if (!sconfig_desc) {
+ dev_err(data->dev, "Not support N driving for pin: %s\n", name);
+ return -ENOTSUPP;
+ }
+ set_val = arg;
+ reg_off = sconfig_desc->reg_offset;
+ n_off = sconfig_desc->ndrive_offset;
+ if (n_off >= 32) {
+ reg_off += 0x4;
+ n_off -= 32;
+ }As per above.
+ mask = GENMASK(n_off + sconfig_desc->ndrive_maskbits - 1, n_off);
As per above.
+ val = set_val << n_off; + rtd_pinctrl_update_bits(data, reg_off, mask, val); + break;
+ case RTD_D_CYCLE:
+ sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr);
+ if (!sconfig_desc || sconfig_desc->dcycle_offset == NA) {
+ dev_err(data->dev, "Not support duty cycle for pin: %s\n", name);
+ return -ENOTSUPP;
+ }
+ set_val = arg;
+ mask = GENMASK(sconfig_desc->dcycle_offset +
+ sconfig_desc->dcycle_maskbits - 1, sconfig_desc->dcycle_offset);Broken indentation and see above.
+ val = set_val << sconfig_desc->dcycle_offset; + rtd_pinctrl_update_bits(data, sconfig_desc->reg_offset, mask, val); + break; + + default: + break; + } + + return 0; +}
...
+static int rtd_pin_config_get(struct pinctrl_dev *pcdev, unsigned int pinnr,
+ unsigned long *config)
+{
+ unsigned int param = pinconf_to_config_param(*config);+ unsigned int arg = 0;
How is this assignment being used?
+
+ switch (param) {
+ default:
+ return -ENOTSUPP;
+ }+ *config = pinconf_to_config_packed(param, arg); + return 0;
This is a dead code, why?!
+}
...
+static int rtd_pin_config_set(struct pinctrl_dev *pcdev, unsigned int pinnr,
+ unsigned long *configs, unsigned int num_configs)
+{
+ struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
+ int i;+ int ret = 0;
How is this assignment being used?
+ for (i = 0; i < num_configs; i++) {
+ ret = rtd_pconf_parse_conf(data, pinnr,
+ pinconf_to_config_param(configs[i]),
+ pinconf_to_config_argument(configs[i]));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}...
+int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc)
+{
+ struct rtd_pinctrl *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->base = of_iomap(pdev->dev.of_node, 0);Use proper platform driver API. Also, why not devm_*()?
+ if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->dev = &pdev->dev; + data->info = desc; + data->desc.name = dev_name(&pdev->dev); + data->desc.pins = data->info->pins; + data->desc.npins = data->info->num_pins; + data->desc.pctlops = &rtd_pinctrl_ops; + data->desc.pmxops = &rtd_pinmux_ops; + data->desc.confops = &rtd_pinconf_ops; + data->desc.custom_params = rtd_custom_bindings; + data->desc.num_custom_params = ARRAY_SIZE(rtd_custom_bindings); + data->desc.owner = THIS_MODULE; + + data->pcdev = pinctrl_register(&data->desc, &pdev->dev, data);
Why not devm_*()?
+ if (!data->pcdev) + return -ENOMEM;
+ platform_set_drvdata(pdev, data);
Is this used anyhow?
+ dev_dbg(&pdev->dev, "probed\n"); + + return 0; +}
...
+EXPORT_SYMBOL(rtd_pinctrl_probe);
Use namespace. ...
+/* + * Copyright (c) 2023 Realtek Semiconductor Corp. + */
One line. ... No ifdeffery guard? ...
+#define NA 0xffffffff
What is this? Bit mask? U32_MAX? -1?
+#define PADDRI_4_8 1 +#define PADDRI_2_4 0
...
+struct rtd_pin_group_desc {
+ const char *name;
+ const unsigned int *pins;
+ unsigned int num_pins;
+};
+
+struct rtd_pin_func_desc {
+ const char *name;
+ const char * const *groups;
+ unsigned int num_groups;
+};NIH struct pingroup, struct pinfunction. -- With Best Regards, Andy Shevchenko