Thread (19 messages) 19 messages, 4 authors, 2023-08-23

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

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help