[v9,2/4] pinctrl: pinmux: Add pinmux-select debugfs file
diff mbox series

Message ID 20210302053059.1049035-3-drew@beagleboard.org
State In Next
Commit 6199f6becc869d30ca9394ca0f7a484bf9d598eb
Headers show
Series
  • pinctrl: pinmux: Add pinmux-select debugfs file
Related show

Commit Message

Drew Fustini March 2, 2021, 5:30 a.m. UTC
Add "pinmux-select" to debugfs which will activate a pin function for a
given pin group:

  echo "<group-name function-name>" > pinmux-select

The write operation pinmux_select() handles this by checking that the
names map to valid selectors and then calling ops->set_mux().

The existing "pinmux-functions" debugfs file lists the pin functions
registered for the pin controller. For example:

  function: pinmux-uart0, groups = [ pinmux-uart0-pins ]
  function: pinmux-mmc0, groups = [ pinmux-mmc0-pins ]
  function: pinmux-mmc1, groups = [ pinmux-mmc1-pins ]
  function: pinmux-i2c0, groups = [ pinmux-i2c0-pins ]
  function: pinmux-i2c1, groups = [ pinmux-i2c1-pins ]
  function: pinmux-spi1, groups = [ pinmux-spi1-pins ]

To activate function pinmux-i2c1 on group pinmux-i2c1-pins:

  echo "pinmux-i2c1-pins pinmux-i2c1" > pinmux-select

Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Tony Lindgren <tony@atomide.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Drew Fustini <drew@beagleboard.org>
---
 drivers/pinctrl/pinmux.c | 102 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)

Comments

Enrico Weigelt, metux IT consult March 12, 2021, 1:57 p.m. UTC | #1
On 02.03.21 06:30, Drew Fustini wrote:

Hi folks,

> Add "pinmux-select" to debugfs which will activate a pin function for a
> given pin group:
> 
>    echo "<group-name function-name>" > pinmux-select
> 
> The write operation pinmux_select() handles this by checking that the
> names map to valid selectors and then calling ops->set_mux().

I've already been playing with similar idea, but for external muxes.
For example, some boards have multiple SIM slots that can be switched
via some gpio pin.

Not sure whether traditional pinmux would be a good match for that.


--mtx
Drew Fustini March 13, 2021, 8:42 a.m. UTC | #2
On Fri, Mar 12, 2021 at 02:57:54PM +0100, Enrico Weigelt, metux IT consult wrote:
> On 02.03.21 06:30, Drew Fustini wrote:
> 
> Hi folks,
> 
> > Add "pinmux-select" to debugfs which will activate a pin function for a
> > given pin group:
> > 
> >    echo "<group-name function-name>" > pinmux-select
> > 
> > The write operation pinmux_select() handles this by checking that the
> > names map to valid selectors and then calling ops->set_mux().
> 
> I've already been playing with similar idea, but for external muxes.
> For example, some boards have multiple SIM slots that can be switched
> via some gpio pin.
> 
> Not sure whether traditional pinmux would be a good match for that.
> 
> 
> --mtx
> 

Thank you for your comment.  I am interested in understanding other
use cases.

Linus can hopefully correct me, but I think this is covered by section
"Drivers needing both pin control and GPIOs" in pinctl.rst [1]. The
driver could be both a GPIO consumer and pinctrl consumer and then be
able to call pinctrl_select_state() and devm_gpio_request().

Thank you,
Drew

[1] https://www.kernel.org/doc/html/latest/driver-api/pinctl.html
Alexandre Belloni March 13, 2021, 9:28 a.m. UTC | #3
On 12/03/2021 14:57:54+0100, Enrico Weigelt, metux IT consult wrote:
> On 02.03.21 06:30, Drew Fustini wrote:
> 
> Hi folks,
> 
> > Add "pinmux-select" to debugfs which will activate a pin function for a
> > given pin group:
> > 
> >    echo "<group-name function-name>" > pinmux-select
> > 
> > The write operation pinmux_select() handles this by checking that the
> > names map to valid selectors and then calling ops->set_mux().
> 
> I've already been playing with similar idea, but for external muxes.
> For example, some boards have multiple SIM slots that can be switched
> via some gpio pin.
> 
> Not sure whether traditional pinmux would be a good match for that.
> 

If you want to be able to use both, then I guess gpio-mux is what you
are looking for. Obviously, it will also require support in the bus
core. On what bus are those SIMs? (I guess the answer will be UART and
then unfortunately UARTs are not represented as busses).
Geert Uytterhoeven March 13, 2021, 6:47 p.m. UTC | #4
Hi Alexandre,

On Sat, Mar 13, 2021 at 10:28 AM Alexandre Belloni
<alexandre.belloni@bootlin.com> wrote:
> On 12/03/2021 14:57:54+0100, Enrico Weigelt, metux IT consult wrote:
> > On 02.03.21 06:30, Drew Fustini wrote:
> > > Add "pinmux-select" to debugfs which will activate a pin function for a
> > > given pin group:
> > >
> > >    echo "<group-name function-name>" > pinmux-select
> > >
> > > The write operation pinmux_select() handles this by checking that the
> > > names map to valid selectors and then calling ops->set_mux().
> >
> > I've already been playing with similar idea, but for external muxes.
> > For example, some boards have multiple SIM slots that can be switched
> > via some gpio pin.
> >
> > Not sure whether traditional pinmux would be a good match for that.
>
> If you want to be able to use both, then I guess gpio-mux is what you
> are looking for. Obviously, it will also require support in the bus
> core. On what bus are those SIMs? (I guess the answer will be UART and
> then unfortunately UARTs are not represented as busses).

We do have support for devices connected to UARTs.
See patternProperties in Documentation/devicetree/bindings/serial/serial.yaml.
Or do you mean something different?

Gr{oetje,eeting}s,

                        Geert
Linus Walleij March 15, 2021, 4:24 p.m. UTC | #5
On Fri, Mar 12, 2021 at 2:57 PM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
> On 02.03.21 06:30, Drew Fustini wrote:
>
> Hi folks,
>
> > Add "pinmux-select" to debugfs which will activate a pin function for a
> > given pin group:
> >
> >    echo "<group-name function-name>" > pinmux-select
> >
> > The write operation pinmux_select() handles this by checking that the
> > names map to valid selectors and then calling ops->set_mux().
>
> I've already been playing with similar idea, but for external muxes.
> For example, some boards have multiple SIM slots that can be switched
> via some gpio pin.
>
> Not sure whether traditional pinmux would be a good match for that.

What is wrong with the subsystem
drivers/mux?

It's exactly for this usecase I think. Peter Rosin already wrote
a GPIO-controlled mux driver too.

Yours,
Linus Walleij
Enrico Weigelt, metux IT consult March 18, 2021, 7:40 p.m. UTC | #6
On 13.03.21 19:47, Geert Uytterhoeven wrote:

>>> I've already been playing with similar idea, but for external muxes.
>>> For example, some boards have multiple SIM slots that can be switched
>>> via some gpio pin.
>>>
>>> Not sure whether traditional pinmux would be a good match for that.
>>
>> If you want to be able to use both, then I guess gpio-mux is what you
>> are looking for. Obviously, it will also require support in the bus
>> core. On what bus are those SIMs? (I guess the answer will be UART and
>> then unfortunately UARTs are not represented as busses).
> 
> We do have support for devices connected to UARTs.
> See patternProperties in Documentation/devicetree/bindings/serial/serial.yaml.
> Or do you mean something different?

in my case, the SIM cards are connected directly to the baseband
(there're extra lines on the m2 slots for that). CPU doesn't ever see
any of this traffic, just can select which SIM card is routed to the
m2 slot via gpio.


--mtx

Patch
diff mbox series

diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index c651b2db0925..f4abfaecfc5c 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -12,6 +12,7 @@ 
  */
 #define pr_fmt(fmt) "pinmux core: " fmt
 
+#include <linux/ctype.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -673,6 +674,105 @@  void pinmux_show_setting(struct seq_file *s,
 DEFINE_SHOW_ATTRIBUTE(pinmux_functions);
 DEFINE_SHOW_ATTRIBUTE(pinmux_pins);
 
+#define PINMUX_SELECT_MAX 128
+static ssize_t pinmux_select(struct file *file, const char __user *user_buf,
+				   size_t len, loff_t *ppos)
+{
+	struct seq_file *sfile = file->private_data;
+	struct pinctrl_dev *pctldev = sfile->private;
+	const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+	const char *const *groups;
+	char *buf, *gname, *fname;
+	unsigned int num_groups;
+	int fsel, gsel, ret;
+
+	if (len > PINMUX_SELECT_MAX)
+		return -ENOMEM;
+
+	buf = kzalloc(PINMUX_SELECT_MAX, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = strncpy_from_user(buf, user_buf, PINMUX_SELECT_MAX);
+	if (ret < 0)
+		goto exit_free_buf;
+	buf[len-1] = '\0';
+
+	/* remove leading and trailing spaces of input buffer */
+	gname = strstrip(buf);
+	if (*gname == '\0') {
+		ret = -EINVAL;
+		goto exit_free_buf;
+	}
+
+	/* find a separator which is a spacelike character */
+	for (fname = gname; !isspace(*fname); fname++) {
+		if (*fname == '\0') {
+			ret = -EINVAL;
+			goto exit_free_buf;
+		}
+	}
+	*fname = '\0';
+
+	/* drop extra spaces between function and group names */
+	fname = skip_spaces(fname + 1);
+	if (*fname == '\0') {
+		ret = -EINVAL;
+		goto exit_free_buf;
+	}
+
+	ret = pinmux_func_name_to_selector(pctldev, fname);
+	if (ret < 0) {
+		dev_err(pctldev->dev, "invalid function %s in map table\n", fname);
+		goto exit_free_buf;
+	}
+	fsel = ret;
+
+	ret = pmxops->get_function_groups(pctldev, fsel, &groups, &num_groups);
+	if (ret) {
+		dev_err(pctldev->dev, "no groups for function %d (%s)", fsel, fname);
+		goto exit_free_buf;
+	}
+
+	ret = match_string(groups, num_groups, gname);
+	if (ret < 0) {
+		dev_err(pctldev->dev, "invalid group %s", gname);
+		goto exit_free_buf;
+	}
+
+	ret = pinctrl_get_group_selector(pctldev, gname);
+	if (ret < 0) {
+		dev_err(pctldev->dev, "failed to get group selector for %s", gname);
+		goto exit_free_buf;
+	}
+	gsel = ret;
+
+	ret = pmxops->set_mux(pctldev, fsel, gsel);
+	if (ret) {
+		dev_err(pctldev->dev, "set_mux() failed: %d", ret);
+		goto exit_free_buf;
+	}
+	ret = len;
+
+exit_free_buf:
+	kfree(buf);
+
+	return ret;
+}
+
+static int pinmux_select_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, NULL, inode->i_private);
+}
+
+static const struct file_operations pinmux_select_ops = {
+	.owner = THIS_MODULE,
+	.open = pinmux_select_open,
+	.write = pinmux_select,
+	.llseek = no_llseek,
+	.release = single_release,
+};
+
 void pinmux_init_device_debugfs(struct dentry *devroot,
 			 struct pinctrl_dev *pctldev)
 {
@@ -680,6 +780,8 @@  void pinmux_init_device_debugfs(struct dentry *devroot,
 			    devroot, pctldev, &pinmux_functions_fops);
 	debugfs_create_file("pinmux-pins", 0444,
 			    devroot, pctldev, &pinmux_pins_fops);
+	debugfs_create_file("pinmux-select", 0200,
+			    devroot, pctldev, &pinmux_select_ops);
 }
 
 #endif /* CONFIG_DEBUG_FS */