diff mbox series

[RFC] pinctrl: pinmux: Add pinmux-set debugfs file

Message ID 20210121051806.623743-1-drew@beagleboard.org
State New
Headers show
Series [RFC] pinctrl: pinmux: Add pinmux-set debugfs file | expand

Commit Message

Drew Fustini Jan. 21, 2021, 5:18 a.m. UTC
This RFC is a change in approach from my previous RFC patch [1]. It adds
"pinnux-set" to debugfs. A function and group on the pin control device
will be activated when 2 integers "<function-selector> <group-selector>"
are written to the file. The debugfs write operation pinmux_set_write()
handles this by calling ops->set_mux() with fsel and gsel.

RFC question: should pinmux-set take function name and group name
instead of the selector numbers?

The following is an example on the PocketBeagle [2] which has the AM3358
SoC and binds to pinctrl-single. I added this to the device tree [3] to
represent two of the pins on the expansion header as an example: P1.36
and P2.01. Both of these header pins are designed to be set to PWM mode
by default [4] but can now be set back to gpio mode through pinmux-set.

	&am33xx_pinmux {

	/* use the pin controller itself as the owner device */
	pinctrl-names = "default",
			"P1_36_gpio", "P1_36_gpio_pu", "P1_36_gpio_pd",
			"P1_36_gpio_input", "P1_36_pwm",
			"P2_01_gpio", "P2_01_gpio_pu", "P2_01_gpio_pd",
			"P2_01_gpio_input", "P2_01_pwm";

	/* set hog for default mode */
	pinctrl-0 = < &P1_36_default_pin &P2_01_default_pin >;
	/* these will create pin functions for each mode */
	pinctrl-1 = <&P1_36_gpio_pin>;
	pinctrl-2 = <&P1_36_gpio_pu_pin>;
	pinctrl-3 = <&P1_36_gpio_pd_pin>;
	pinctrl-4 = <&P1_36_gpio_input_pin>;
	pinctrl-5 = <&P1_36_pwm_pin>;
	pinctrl-6 = <&P2_01_default_pin>;
	pinctrl-7 = <&P2_01_gpio_pin>;
	pinctrl-8 = <&P2_01_gpio_pu_pin>;
	pinctrl-9 = <&P2_01_gpio_pd_pin>;
	pinctrl-10 = <&P2_01_gpio_input_pin>;
	pinctrl-11 = <&P2_01_pwm_pin>;

	/* P1_36 (ZCZ ball A13) ehrpwm0a */
	P1_36_default_pin: pinmux_P1_36_default_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; };
	P1_36_gpio_pin: pinmux_P1_36_gpio_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; };
	P1_36_gpio_pu_pin: pinmux_P1_36_gpio_pu_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; };
	P1_36_gpio_pd_pin: pinmux_P1_36_gpio_pd_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; };
	P1_36_gpio_input_pin: pinmux_P1_36_gpio_input_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE7) >; };
	P1_36_pwm_pin: pinmux_P1_36_pwm_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1) >; };
	P1_36_spi_sclk_pin: pinmux_P1_36_spi_sclk_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) >; };
	P1_36_pruout_pin: pinmux_P1_36_pruout_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5) >; };
	P1_36_pruin_pin: pinmux_P1_36_pruin_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0990, PIN_INPUT | MUX_MODE6) >; };

	/* P2_01 (ZCZ ball U14) ehrpwm1a */
	P2_01_default_pin: pinmux_P2_01_default_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; };
	P2_01_gpio_pin: pinmux_P2_01_gpio_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0848, PIN_OUTPUT | INPUT_EN | MUX_MODE7) >; };
	P2_01_gpio_pu_pin: pinmux_P2_01_gpio_pu_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) >; };
	P2_01_gpio_pd_pin: pinmux_P2_01_gpio_pd_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7) >; };
	P2_01_gpio_input_pin: pinmux_P2_01_gpio_input_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0848, PIN_INPUT | MUX_MODE7) >; };
	P2_01_pwm_pin: pinmux_P2_01_pwm_pin { pinctrl-single,pins = <
		AM33XX_IOPAD(0x0848, PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6) >; };

The following shows the pin functions registered for the pin controller:

root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# cat pinmux-functions
function: pinmux_P1_36_default_pin, groups = [ pinmux_P1_36_default_pin ]
function: pinmux_P2_01_default_pin, groups = [ pinmux_P2_01_default_pin ]
function: pinmux_P1_36_gpio_pin, groups = [ pinmux_P1_36_gpio_pin ]
function: pinmux_P1_36_gpio_pu_pin, groups = [ pinmux_P1_36_gpio_pu_pin ]
function: pinmux_P1_36_gpio_pd_pin, groups = [ pinmux_P1_36_gpio_pd_pin ]
function: pinmux_P1_36_gpio_input_pin, groups = [ pinmux_P1_36_gpio_input_pin ]
function: pinmux_P1_36_pwm_pin, groups = [ pinmux_P1_36_pwm_pin ]
function: pinmux_P2_01_gpio_pin, groups = [ pinmux_P2_01_gpio_pin ]
function: pinmux_P2_01_gpio_pu_pin, groups = [ pinmux_P2_01_gpio_pu_pin ]
function: pinmux_P2_01_gpio_pd_pin, groups = [ pinmux_P2_01_gpio_pd_pin ]
function: pinmux_P2_01_gpio_input_pin, groups = [ pinmux_P2_01_gpio_input_pin ]
function: pinmux_P2_01_pwm_pin, groups = [ pinmux_P2_01_pwm_pin ]
function: pinmux-uart0-pins, groups = [ pinmux-uart0-pins ]
function: pinmux-mmc0-pins, groups = [ pinmux-mmc0-pins ]
function: pinmux-i2c0-pins, groups = [ pinmux-i2c0-pins ]

Activate the pinmux_P1_36_gpio_pin function (fsel 2):

root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# echo '2 2' > pinmux-set

Extra debug output that I added shows that pinctrl-single's set_mux()
has set the register correctly for gpio mode:

pinmux core: DEBUG pinmux_set_write(): returned 0
pinmux core: DEBUG pinmux_set_write(): buf=[2 2]
pinmux core: DEBUG pinmux_set_write(): sscanf(2,2)
pinmux core: DEBUG pinmux_set_write(): call ops->set_mux(fsel=2, gsel=2)
pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): call pinmux_generic_get_function() on fselector=2
pinctrl-single 44e10800.pinmux: enabling (null) function2
pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): func->nvals=1
pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): offset=0x190 old_val=0x21 val=0x2f

Activate the pinmux_P1_36_pwm_pin function (fsel 6):

root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# echo '6 6' > pinmux-set

pinctrl-single set_mux() is able to set register correctly for pwm mode:

pinmux core: DEBUG pinmux_set_write(): returned 0
pinmux core: DEBUG pinmux_set_write(): buf=[6 6]
pinmux core: DEBUG pinmux_set_write(): sscanf(6,6)
pinmux core: DEBUG pinmux_set_write(): call ops->set_mux(fsel=6, gsel=6)
pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): call pinmux_generic_get_function() on fselector=6
pinctrl-single 44e10800.pinmux: enabling (null) function6
pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): func->nvals=1
pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): offset=0x190 old_val=0x2f val=0x21

I would appreciate any feedback on this approach.  Thank you!

-Drew

[1] https://lore.kernel.org/linux-gpio/20201218045134.4158709-1-drew@beagleboard.org/
[2] https://beagleboard.org/pocket
[3] arch/arm/boot/dts/am335x-pocketbeagle.dts
[4] https://github.com/beagleboard/pocketbeagle/wiki/System-Reference-Manual#70-connectors-

Cc: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
Cc: Jason Kridner <jkridner@beagleboard.org>,
Cc: Robert Nelson <robertcnelson@beagleboard.org>,
Cc: Linus Walleij <linus.walleij@linaro.org>,
Cc: Tony Lindgren <tony@atomide.com>,
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Drew Fustini <drew@beagleboard.org>
---
 drivers/pinctrl/pinmux.c | 65 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

Comments

Andy Shevchenko Jan. 21, 2021, 11:18 a.m. UTC | #1
On Thu, Jan 21, 2021 at 7:18 AM Drew Fustini <drew@beagleboard.org> wrote:
>
> This RFC is a change in approach from my previous RFC patch [1]. It adds
> "pinnux-set" to debugfs. A function and group on the pin control device
> will be activated when 2 integers "<function-selector> <group-selector>"
> are written to the file. The debugfs write operation pinmux_set_write()
> handles this by calling ops->set_mux() with fsel and gsel.

s/ops//

> RFC question: should pinmux-set take function name and group name
> instead of the selector numbers?

I would prefer names and integers (but from user p.o.v. names are
easier to understand, while numbers are good for scripting).

The following is better to include in documentation and remove from
the commit message.

> The following is an example on the PocketBeagle [2] which has the AM3358
> SoC and binds to pinctrl-single. I added this to the device tree [3] to
> represent two of the pins on the expansion header as an example: P1.36
> and P2.01. Both of these header pins are designed to be set to PWM mode
> by default [4] but can now be set back to gpio mode through pinmux-set.

...

> The following shows the pin functions registered for the pin controller:
>
> root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# cat pinmux-functions

Shorter is better, what about simply

# cat /sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single/pinmux-functions
?

Btw  in reST format you may create a nice citation of this. And yes,
this should also go to the documentation.

> function: pinmux_P1_36_default_pin, groups = [ pinmux_P1_36_default_pin ]
> function: pinmux_P2_01_default_pin, groups = [ pinmux_P2_01_default_pin ]
> function: pinmux_P1_36_gpio_pin, groups = [ pinmux_P1_36_gpio_pin ]
> function: pinmux_P1_36_gpio_pu_pin, groups = [ pinmux_P1_36_gpio_pu_pin ]
> function: pinmux_P1_36_gpio_pd_pin, groups = [ pinmux_P1_36_gpio_pd_pin ]
> function: pinmux_P1_36_gpio_input_pin, groups = [ pinmux_P1_36_gpio_input_pin ]
> function: pinmux_P1_36_pwm_pin, groups = [ pinmux_P1_36_pwm_pin ]
> function: pinmux_P2_01_gpio_pin, groups = [ pinmux_P2_01_gpio_pin ]
> function: pinmux_P2_01_gpio_pu_pin, groups = [ pinmux_P2_01_gpio_pu_pin ]
> function: pinmux_P2_01_gpio_pd_pin, groups = [ pinmux_P2_01_gpio_pd_pin ]
> function: pinmux_P2_01_gpio_input_pin, groups = [ pinmux_P2_01_gpio_input_pin ]
> function: pinmux_P2_01_pwm_pin, groups = [ pinmux_P2_01_pwm_pin ]
> function: pinmux-uart0-pins, groups = [ pinmux-uart0-pins ]
> function: pinmux-mmc0-pins, groups = [ pinmux-mmc0-pins ]
> function: pinmux-i2c0-pins, groups = [ pinmux-i2c0-pins ]
>
> Activate the pinmux_P1_36_gpio_pin function (fsel 2):
>
> root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# echo '2 2' > pinmux-set
>
> Extra debug output that I added shows that pinctrl-single's set_mux()
> has set the register correctly for gpio mode:
>
> pinmux core: DEBUG pinmux_set_write(): returned 0
> pinmux core: DEBUG pinmux_set_write(): buf=[2 2]
> pinmux core: DEBUG pinmux_set_write(): sscanf(2,2)
> pinmux core: DEBUG pinmux_set_write(): call ops->set_mux(fsel=2, gsel=2)
> pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): call pinmux_generic_get_function() on fselector=2
> pinctrl-single 44e10800.pinmux: enabling (null) function2
> pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): func->nvals=1
> pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): offset=0x190 old_val=0x21 val=0x2f
>
> Activate the pinmux_P1_36_pwm_pin function (fsel 6):
>
> root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# echo '6 6' > pinmux-set
>
> pinctrl-single set_mux() is able to set register correctly for pwm mode:
>
> pinmux core: DEBUG pinmux_set_write(): returned 0
> pinmux core: DEBUG pinmux_set_write(): buf=[6 6]
> pinmux core: DEBUG pinmux_set_write(): sscanf(6,6)
> pinmux core: DEBUG pinmux_set_write(): call ops->set_mux(fsel=6, gsel=6)
> pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): call pinmux_generic_get_function() on fselector=6
> pinctrl-single 44e10800.pinmux: enabling (null) function6
> pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): func->nvals=1
> pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): offset=0x190 old_val=0x2f val=0x21

This and above is still part of documentation, and not a commit message thingy.

...

> +static ssize_t pinmux_set_write(struct file *file, const char __user *user_buf,
> +                                  size_t cnt, loff_t *ppos)
> +{
> +       int err;
> +       int fsel;
> +       int gsel;
> +       int ret;
> +       char *buf;
> +       struct seq_file *sfile;
> +       struct pinctrl_dev *pctldev;
> +       const struct pinmux_ops *ops;

Reversed xmas tree order please, and you may group some of them, like

   int fsel, gsel;

> +       if (*ppos != 0)
> +               return -EINVAL;

> +       if (cnt == 0)
> +               return 0;

Has it ever happened here?

> +       buf = memdup_user_nul(user_buf, cnt);
> +       if (IS_ERR(buf))
> +               return PTR_ERR(buf);
> +
> +       if (buf[cnt - 1] == '\n')
> +               buf[cnt - 1] = '\0';

Shouldn't you rather use strndup_from_user() (or how is it called?)

> +       ret = sscanf(buf, "%d %d", &fsel, &gsel);
> +       if (ret != 2) {
> +               pr_warn("%s: sscanf() expects '<fsel> <gsel>'", __func__);

No __func__ and instead use dev_err() (it is strange you are using
warn level for errors).

> +               err = -EINVAL;
> +               goto err_freebuf;
> +       }

> +       sfile = file->private_data;
> +       pctldev = sfile->private;

These can be applied directly in the definition block above.

> +       ops = pctldev->desc->pmxops;
> +       ret = ops->set_mux(pctldev, fsel, gsel);

> +       if (ret != 0) {

if (ret)

> +               pr_warn("%s(): set_mux() failed: %d", __func__, ret);

As above.

> +               err = -EINVAL;
> +               goto err_freebuf;
> +       }

> +       kfree(buf);
> +       return cnt;
> +
> +err_freebuf:
> +       kfree(buf);
> +       return err;

Can be simply

 err_freebuf:
        kfree(buf);
        return err ?: cnt;

> +}
> +

...

> +       debugfs_create_file("pinmux-set", S_IFREG | S_IWUSR,
> +                           devroot, pctldev, &pinmux_set_ops);

I would rather call it 'pinmux-select'.

Overall since it's a debugfs I do not much care about interfaces and
particular implementation details, but in general looks good to me,
thanks for doing this!
Drew Fustini Jan. 21, 2021, 11:26 p.m. UTC | #2
On Thu, Jan 21, 2021 at 01:18:58PM +0200, Andy Shevchenko wrote:
> On Thu, Jan 21, 2021 at 7:18 AM Drew Fustini <drew@beagleboard.org> wrote:
> >
> > This RFC is a change in approach from my previous RFC patch [1]. It adds
> > "pinnux-set" to debugfs. A function and group on the pin control device
> > will be activated when 2 integers "<function-selector> <group-selector>"
> > are written to the file. The debugfs write operation pinmux_set_write()
> > handles this by calling ops->set_mux() with fsel and gsel.
> 
> s/ops//

Ok, thanks.
> 
> > RFC question: should pinmux-set take function name and group name
> > instead of the selector numbers?
> 
> I would prefer names and integers (but from user p.o.v. names are
> easier to understand, while numbers are good for scripting).

I don't actually see any example of looking up the function name in the
existing pinctrl code. There is pin_function_tree in struct pinctrl_dev.
pinmux_generic_get_function_name() does radix_tree_lookup() with the
selector integer as the key, but there is no corresponding "get function
selector by name" function.

I think I would need to go through all the nodes in the radix tree to
find the name that matches. Although, I am just learning now about the
radix implementation in Linux so there might be a simpler way that I am
missing.

> 
> The following is better to include in documentation and remove from
> the commit message.
> 
> > The following is an example on the PocketBeagle [2] which has the AM3358
> > SoC and binds to pinctrl-single. I added this to the device tree [3] to
> > represent two of the pins on the expansion header as an example: P1.36
> > and P2.01. Both of these header pins are designed to be set to PWM mode
> > by default [4] but can now be set back to gpio mode through pinmux-set.
> 
> ...
> 
> > The following shows the pin functions registered for the pin controller:
> >
> > root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# cat pinmux-functions
> 
> Shorter is better, what about simply
> 
> # cat /sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single/pinmux-functions
> ?
> 
> Btw  in reST format you may create a nice citation of this. And yes,
> this should also go to the documentation.

Good point, I'll shorten the example lines in v2.

> > function: pinmux_P1_36_default_pin, groups = [ pinmux_P1_36_default_pin ]
> > function: pinmux_P2_01_default_pin, groups = [ pinmux_P2_01_default_pin ]
> > function: pinmux_P1_36_gpio_pin, groups = [ pinmux_P1_36_gpio_pin ]
> > function: pinmux_P1_36_gpio_pu_pin, groups = [ pinmux_P1_36_gpio_pu_pin ]
> > function: pinmux_P1_36_gpio_pd_pin, groups = [ pinmux_P1_36_gpio_pd_pin ]
> > function: pinmux_P1_36_gpio_input_pin, groups = [ pinmux_P1_36_gpio_input_pin ]
> > function: pinmux_P1_36_pwm_pin, groups = [ pinmux_P1_36_pwm_pin ]
> > function: pinmux_P2_01_gpio_pin, groups = [ pinmux_P2_01_gpio_pin ]
> > function: pinmux_P2_01_gpio_pu_pin, groups = [ pinmux_P2_01_gpio_pu_pin ]
> > function: pinmux_P2_01_gpio_pd_pin, groups = [ pinmux_P2_01_gpio_pd_pin ]
> > function: pinmux_P2_01_gpio_input_pin, groups = [ pinmux_P2_01_gpio_input_pin ]
> > function: pinmux_P2_01_pwm_pin, groups = [ pinmux_P2_01_pwm_pin ]
> > function: pinmux-uart0-pins, groups = [ pinmux-uart0-pins ]
> > function: pinmux-mmc0-pins, groups = [ pinmux-mmc0-pins ]
> > function: pinmux-i2c0-pins, groups = [ pinmux-i2c0-pins ]
> >
> > Activate the pinmux_P1_36_gpio_pin function (fsel 2):
> >
> > root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# echo '2 2' > pinmux-set
> >
> > Extra debug output that I added shows that pinctrl-single's set_mux()
> > has set the register correctly for gpio mode:
> >
> > pinmux core: DEBUG pinmux_set_write(): returned 0
> > pinmux core: DEBUG pinmux_set_write(): buf=[2 2]
> > pinmux core: DEBUG pinmux_set_write(): sscanf(2,2)
> > pinmux core: DEBUG pinmux_set_write(): call ops->set_mux(fsel=2, gsel=2)
> > pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): call pinmux_generic_get_function() on fselector=2
> > pinctrl-single 44e10800.pinmux: enabling (null) function2
> > pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): func->nvals=1
> > pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): offset=0x190 old_val=0x21 val=0x2f
> >
> > Activate the pinmux_P1_36_pwm_pin function (fsel 6):
> >
> > root@beaglebone:/sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single# echo '6 6' > pinmux-set
> >
> > pinctrl-single set_mux() is able to set register correctly for pwm mode:
> >
> > pinmux core: DEBUG pinmux_set_write(): returned 0
> > pinmux core: DEBUG pinmux_set_write(): buf=[6 6]
> > pinmux core: DEBUG pinmux_set_write(): sscanf(6,6)
> > pinmux core: DEBUG pinmux_set_write(): call ops->set_mux(fsel=6, gsel=6)
> > pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): call pinmux_generic_get_function() on fselector=6
> > pinctrl-single 44e10800.pinmux: enabling (null) function6
> > pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): func->nvals=1
> > pinctrl-single 44e10800.pinmux: DEBUG pcs_set_mux(): offset=0x190 old_val=0x2f val=0x21
> 
> This and above is still part of documentation, and not a commit message thingy.

Is something I should add to Documentation/driver-api/pinctl.rst in a
seperate patch?

> 
> ...
> 
> > +static ssize_t pinmux_set_write(struct file *file, const char __user *user_buf,
> > +                                  size_t cnt, loff_t *ppos)
> > +{
> > +       int err;
> > +       int fsel;
> > +       int gsel;
> > +       int ret;
> > +       char *buf;
> > +       struct seq_file *sfile;
> > +       struct pinctrl_dev *pctldev;
> > +       const struct pinmux_ops *ops;
> 
> Reversed xmas tree order please, and you may group some of them, like
> 
>    int fsel, gsel;
> 

Ok, understood.

> > +       if (*ppos != 0)
> > +               return -EINVAL;
> 
> > +       if (cnt == 0)
> > +               return 0;
> 
> Has it ever happened here?

Good point, I guess there is no reason for userspace to write 0 bytes.

> > +       buf = memdup_user_nul(user_buf, cnt);
> > +       if (IS_ERR(buf))
> > +               return PTR_ERR(buf);
> > +
> > +       if (buf[cnt - 1] == '\n')
> > +               buf[cnt - 1] = '\0';
> 
> Shouldn't you rather use strndup_from_user() (or how is it called?)
> 
> > +       ret = sscanf(buf, "%d %d", &fsel, &gsel);
> > +       if (ret != 2) {
> > +               pr_warn("%s: sscanf() expects '<fsel> <gsel>'", __func__);
> 
> No __func__ and instead use dev_err() (it is strange you are using
> warn level for errors).
> 

Ok, that makes sense. I used warn because I wasn't sure if bad format in
a write to a debugfs file rises to the level of error.

> > +               err = -EINVAL;
> > +               goto err_freebuf;
> > +       }
> 
> > +       sfile = file->private_data;
> > +       pctldev = sfile->private;
> 
> These can be applied directly in the definition block above.

I'll clean that up.

> 
> > +       ops = pctldev->desc->pmxops;
> > +       ret = ops->set_mux(pctldev, fsel, gsel);
> 
> > +       if (ret != 0) {
> 
> if (ret)
> 
> > +               pr_warn("%s(): set_mux() failed: %d", __func__, ret);
> 
> As above.
> 
> > +               err = -EINVAL;
> > +               goto err_freebuf;
> > +       }
> 
> > +       kfree(buf);
> > +       return cnt;
> > +
> > +err_freebuf:
> > +       kfree(buf);
> > +       return err;
> 
> Can be simply
> 
>  err_freebuf:
>         kfree(buf);
>         return err ?: cnt;

Thanks, I didn't really like the duplication but was having trouble
thinking of a cleaner way to write it.  That is good to know it is ok to
use the ternary operator in a return statement.

> 
> > +}
> > +
> 
> ...
> 
> > +       debugfs_create_file("pinmux-set", S_IFREG | S_IWUSR,
> > +                           devroot, pctldev, &pinmux_set_ops);
> 
> I would rather call it 'pinmux-select'.

I think that makes sense, too.
> 
> Overall since it's a debugfs I do not much care about interfaces and
> particular implementation details, but in general looks good to me,
> thanks for doing this!

Thanks for the review.  I'll get a v2 posted.

-Drew
Andy Shevchenko Jan. 22, 2021, 9:50 a.m. UTC | #3
On Fri, Jan 22, 2021 at 1:26 AM Drew Fustini <drew@beagleboard.org> wrote:
> On Thu, Jan 21, 2021 at 01:18:58PM +0200, Andy Shevchenko wrote:
> > On Thu, Jan 21, 2021 at 7:18 AM Drew Fustini <drew@beagleboard.org> wrote:

...

> > > RFC question: should pinmux-set take function name and group name
> > > instead of the selector numbers?
> >
> > I would prefer names and integers (but from user p.o.v. names are
> > easier to understand, while numbers are good for scripting).
>
> I don't actually see any example of looking up the function name in the
> existing pinctrl code. There is pin_function_tree in struct pinctrl_dev.
> pinmux_generic_get_function_name() does radix_tree_lookup() with the
> selector integer as the key, but there is no corresponding "get function
> selector by name" function.
>
> I think I would need to go through all the nodes in the radix tree to
> find the name that matches. Although, I am just learning now about the
> radix implementation in Linux so there might be a simpler way that I am
> missing.

I probably have to revive my work towards gluing ACPI with pin control
where AFAIR I have created some kind of radix / rbtree for something
(not sure it's exactly what you need here, so consider this just as a
side note).

...

> > The following is better to include in documentation and remove from
> > the commit message.

> > Shorter is better, what about simply
> >
> > # cat /sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single/pinmux-functions
> > ?
> >
> > Btw  in reST format you may create a nice citation of this. And yes,
> > this should also go to the documentation.
>
> Good point, I'll shorten the example lines in v2.

Even better to tell that we operate on the level of mount point of
debugfs and use

 # cat pinctrl/44e10800.pinmux-pinctrl-single/pinmux-functions

> > This and above is still part of documentation, and not a commit message thingy.
>
> Is something I should add to Documentation/driver-api/pinctl.rst in a
> seperate patch?

Not sure, I think more about as a part of this very path you change
code and documentation. But usually it's a preference of the certain
subsystem.

...

> > > +       if (cnt == 0)
> > > +               return 0;
> >
> > Has it ever happened here?
>
> Good point, I guess there is no reason for userspace to write 0 bytes.

My point is that this check is done somewhere in the guts of kernfs.
When in doubt I recommend to look around in the kernel and check most
recent code with similar code pieces.

...

> > > +       buf = memdup_user_nul(user_buf, cnt);
> > > +       if (IS_ERR(buf))
> > > +               return PTR_ERR(buf);
> > > +
> > > +       if (buf[cnt - 1] == '\n')
> > > +               buf[cnt - 1] = '\0';
> >
> > Shouldn't you rather use strndup_from_user() (or how is it called?)

Any comments?

...

> > Can be simply
> >
> >  err_freebuf:
> >         kfree(buf);
> >         return err ?: cnt;
>
> Thanks, I didn't really like the duplication but was having trouble
> thinking of a cleaner way to write it.  That is good to know it is ok to
> use the ternary operator in a return statement.

Again, depends on certain subsystem maintainer's preferences.


> > > +       debugfs_create_file("pinmux-set", S_IFREG | S_IWUSR,
> > > +                           devroot, pctldev, &pinmux_set_ops);

One more thing, as a preparatory patch please move from S_I* to plain
octal numbers as it's preferable.
Drew Fustini Jan. 22, 2021, 11:16 p.m. UTC | #4
On Fri, Jan 22, 2021 at 11:50:52AM +0200, Andy Shevchenko wrote:
> On Fri, Jan 22, 2021 at 1:26 AM Drew Fustini <drew@beagleboard.org> wrote:
> > On Thu, Jan 21, 2021 at 01:18:58PM +0200, Andy Shevchenko wrote:
> > > On Thu, Jan 21, 2021 at 7:18 AM Drew Fustini <drew@beagleboard.org> wrote:
> 
> ...
> 
> > > > RFC question: should pinmux-set take function name and group name
> > > > instead of the selector numbers?
> > >
> > > I would prefer names and integers (but from user p.o.v. names are
> > > easier to understand, while numbers are good for scripting).
> >
> > I don't actually see any example of looking up the function name in the
> > existing pinctrl code. There is pin_function_tree in struct pinctrl_dev.
> > pinmux_generic_get_function_name() does radix_tree_lookup() with the
> > selector integer as the key, but there is no corresponding "get function
> > selector by name" function.
> >
> > I think I would need to go through all the nodes in the radix tree to
> > find the name that matches. Although, I am just learning now about the
> > radix implementation in Linux so there might be a simpler way that I am
> > missing.
> 
> I probably have to revive my work towards gluing ACPI with pin control
> where AFAIR I have created some kind of radix / rbtree for something
> (not sure it's exactly what you need here, so consider this just as a
> side note).
> 
> ...
> 
> > > The following is better to include in documentation and remove from
> > > the commit message.
> 
> > > Shorter is better, what about simply
> > >
> > > # cat /sys/kernel/debug/pinctrl/44e10800.pinmux-pinctrl-single/pinmux-functions
> > > ?
> > >
> > > Btw  in reST format you may create a nice citation of this. And yes,
> > > this should also go to the documentation.
> >
> > Good point, I'll shorten the example lines in v2.
> 
> Even better to tell that we operate on the level of mount point of
> debugfs and use
> 
>  # cat pinctrl/44e10800.pinmux-pinctrl-single/pinmux-functions
> 
> > > This and above is still part of documentation, and not a commit message thingy.
> >
> > Is something I should add to Documentation/driver-api/pinctl.rst in a
> > seperate patch?
> 
> Not sure, I think more about as a part of this very path you change
> code and documentation. But usually it's a preference of the certain
> subsystem.
> 
> ...
> 
> > > > +       if (cnt == 0)
> > > > +               return 0;
> > >
> > > Has it ever happened here?
> >
> > Good point, I guess there is no reason for userspace to write 0 bytes.
> 
> My point is that this check is done somewhere in the guts of kernfs.
> When in doubt I recommend to look around in the kernel and check most
> recent code with similar code pieces.
> 
> ...
> 
> > > > +       buf = memdup_user_nul(user_buf, cnt);
> > > > +       if (IS_ERR(buf))
> > > > +               return PTR_ERR(buf);
> > > > +
> > > > +       if (buf[cnt - 1] == '\n')
> > > > +               buf[cnt - 1] = '\0';
> > >
> > > Shouldn't you rather use strndup_from_user() (or how is it called?)
> 
> Any comments?

Sorry, I had meant to comment on that.  I tried strndup_user() but had
difficulty in using it as 'length > n' was always true and thus returned
an error.  There are not that many users of it either.

I've switched to this based on how armada_debugfs_crtc_reg_write() in 
armada_debugfs.c handles the user writing multiple integers:

        char buf[16];

        if (cnt > sizeof(buf) - 1)    
                cnt = sizeof(buf) - 1;
        ret = strncpy_from_user(buf, user_buf, cnt);
        if (ret < 0)
                return ret;
        buf[cnt] = '\0';
        if (buf[cnt - 1] == '\n')
                buf[cnt - 1] = '\0';  
	// the parse with sscanf()

I choose 16 for buf as two integer numbers seperated by a space should
never be more than 16.  I suppose the downside is that it is allocated
on the stack but it is a small buffer.

I'll post v2 so it can be evaluated in the full patch context.

> 
> ...
> 
> > > Can be simply
> > >
> > >  err_freebuf:
> > >         kfree(buf);
> > >         return err ?: cnt;
> >
> > Thanks, I didn't really like the duplication but was having trouble
> > thinking of a cleaner way to write it.  That is good to know it is ok to
> > use the ternary operator in a return statement.
> 
> Again, depends on certain subsystem maintainer's preferences.
> 
> 
> > > > +       debugfs_create_file("pinmux-set", S_IFREG | S_IWUSR,
> > > > +                           devroot, pctldev, &pinmux_set_ops);
> 
> One more thing, as a preparatory patch please move from S_I* to plain
> octal numbers as it's preferable.
> 
> 
> -- 
> With Best Regards,
> Andy Shevchenko
diff mbox series

Patch

diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index bab888fe3f8e..300e2b3d0ea8 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -673,6 +673,69 @@  void pinmux_show_setting(struct seq_file *s,
 DEFINE_SHOW_ATTRIBUTE(pinmux_functions);
 DEFINE_SHOW_ATTRIBUTE(pinmux_pins);
 
+static ssize_t pinmux_set_write(struct file *file, const char __user *user_buf,
+				   size_t cnt, loff_t *ppos)
+{
+	int err;
+	int fsel;
+	int gsel;
+	int ret;
+	char *buf;
+	struct seq_file *sfile;
+	struct pinctrl_dev *pctldev;
+	const struct pinmux_ops *ops;
+
+	if (*ppos != 0)
+		return -EINVAL;
+
+	if (cnt == 0)
+		return 0;
+
+	buf = memdup_user_nul(user_buf, cnt);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	if (buf[cnt - 1] == '\n')
+		buf[cnt - 1] = '\0';
+
+	ret = sscanf(buf, "%d %d", &fsel, &gsel);
+	if (ret != 2) {
+		pr_warn("%s: sscanf() expects '<fsel> <gsel>'", __func__);
+		err = -EINVAL;
+		goto err_freebuf;
+	}
+
+	sfile = file->private_data;
+	pctldev = sfile->private;
+	ops = pctldev->desc->pmxops;
+	ret = ops->set_mux(pctldev, fsel, gsel);
+	if (ret != 0) {
+		pr_warn("%s(): set_mux() failed: %d", __func__, ret);
+		err = -EINVAL;
+		goto err_freebuf;
+	}
+	kfree(buf);
+	return cnt;
+
+err_freebuf:
+	kfree(buf);
+	return err;
+}
+
+static int pinmux_set_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, NULL, inode->i_private);
+}
+
+static const struct file_operations pinmux_set_ops = {
+	.owner = THIS_MODULE,
+	.open = pinmux_set_open,
+	.read = seq_read,
+	.write = pinmux_set_write,
+	.llseek = no_llseek,
+	.release = single_release,
+};
+
 void pinmux_init_device_debugfs(struct dentry *devroot,
 			 struct pinctrl_dev *pctldev)
 {
@@ -680,6 +743,8 @@  void pinmux_init_device_debugfs(struct dentry *devroot,
 			    devroot, pctldev, &pinmux_functions_fops);
 	debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO,
 			    devroot, pctldev, &pinmux_pins_fops);
+	debugfs_create_file("pinmux-set", S_IFREG | S_IWUSR,
+			    devroot, pctldev, &pinmux_set_ops);
 }
 
 #endif /* CONFIG_DEBUG_FS */