Re: Pinconf issues on AMxxx plattforms

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2023-05-04 at 08:35 +0300, Tony Lindgren wrote:
> Hi,
> 
> Adding linux-gpio and Linus W to Cc. Some questions and comments below
> towards the end.
> 
> * Niedermayr, BENEDIKT <benedikt.niedermayr@xxxxxxxxxxx> [230503 08:38]:
> > Hello,
> > 
> > We encountered some issues when accessing the gpiochardev interface on an
> > AM65xx plaform.
> >  
> > The basic idea was to fully rely on all features the gpiochardev seems to
> > offer. 
> > I got all relevant information from the Linux Kernel Documentation
> > (Documentation/driver-api/pin-control.rst) and saw 
> > some presentations from Linus Walleij  regarding the gpiochardev
> > capabilities.
> > 
> > If I understand that correctly the gpiochardev interface should support the
> > following features:
> > * Requesting gpio pins from userspace
> > * Set input/output directions
> > * Set BIAS settings (pull-up, pull-down, etc.)
> > * Gpio function of that pin automatically gets muxed in if requested 
> > 
> > Requesting pins worked for me as expected after I added the required DTB
> > properties:
> > * pinctrl-single: Add each required pin to "pinctrl-single,gpio-range" in
> > the pincontroller node 
> > * gpio: Add each required pin to gpio-range property in the gpio node
> > 
> > I also added the required childnodes in the pinctrl node:
> > 
> > &main_pmx0 {
> >   ... 
> >    d6_gpio: d6-gpio {
> > pinctrl-single,pins = <
> > /* (AH16) GPIO0_38 */
> > AM65X_IOPAD(0x0098, PIN_INPUT, 7)
> > > ;
> > pinctrl-single,bias-pullup   = <0x20000  0x20000  0x10000  0x30000>;
> > pinctrl-single,bias-pulldown = <0x00000  0x0      0x10000  0x30000>;
> > };
> > ...
> > }
> > 
> > When I tried to set any BIAS settings nothing happened or some error occured
> > in the kernel logs (i'm not 100% sure anymore since almost 2 months have
> > past). 
> > The first thing I had to do was to assign the gpiochip_generic_config
> > callback to the gpiochiop for that (gpio-davinci.c). This callback in turn
> > will finally call pcs_pinconf_set(), which
> > is the pinctrl drivers related callback for setting the pinconf.
> > But still no success...
> > 
> > Then I went deeper into the rabbit whole and encountered that the error had
> > to do with pcs_get_function() (pinctrl-single.c).
> > I found out that this function requests the current function (or pinmux
> > state) from the pinctrl subsystem core. 
> > The pinctrl driver needs this information for accessing the correct pinctrl
> > childnode bits. 
> > 
> > So what is the Problem here?
> > The pinctrl offers 3 different options for muxing:
> > 
> > 1.  Using the generic kernel APIs: 
> >      Call pinctrl_select_state() function as stated
> > in  Documentation/driver-api/pin-control.rst (section "Pin control requests
> > from drivers").
> >      This function will select a defined state which has been defined in DTB
> > with "pinctrl-0", "pinctrl-1", "pinctrl-x"
> > 2.  Mux pins with debugfs:
> >      Write the desired pingroup and pinfunction into the "pinmux-select"
> > file of the related pin controller.
> > 3. Mux the GPIO function of a requested GPIO pin by calling the pinctrl
> > drivers pcs_request_gpio() function.
> > 
> > The problem now is that only option 1. will store the current mux
> > information in the pinctrl subsystems core. 
> > The pinctrl-single driver highly depends on that information, which is not
> > available at all wenn muxing with options 2&3.
> > 
> > I was able to fix that for option 2 but not for option 3. The problem here
> > is that the pcs_request_gpio() function just does not provide enough
> > parameters with sufficient information for achieving that task.
> 
> Care to post what your fix for #2 above looks like?

Thanks for the reply.

Sure, I can post it. But that is just an example and not a final proposal.

It seems pretty complex but accessing "pdesc->mux_setting" directly was not possible, since
it is defined as const pointer. 

So I created an internal shadow copy private data struct and stored the mux_settings there.
In pcs_set_mux() I can then update the mux_setting and query it in pcs_get_function().

Im sure that there are much better solutions but for now it's just a first shot.


diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index cd314a5305a2..fa47fbf1534c 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -38,6 +38,16 @@
 #define DRIVER_NAME			"pinctrl-single"
 #define PCS_OFF_DISABLED		~0U
 
+/**
+ * struct pcs_setting_mux - hold current mux settings per pin
+ * @func: 	selected pin function
+ * @group:	selected pin group
+ */
+struct pcs_setting_mux {
+	unsigned func;
+	unsigned group;
+};
+
 /**
  * struct pcs_func_vals - mux function register offset and value pair
  * @reg:	register virtual address
@@ -340,12 +350,11 @@ static int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin,
 {
 	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
 	struct pin_desc *pdesc = pin_desc_get(pctldev, pin);
-	const struct pinctrl_setting_mux *setting;
+	struct pcs_setting_mux *setting = pdesc->drv_data;
 	struct function_desc *function;
 	unsigned fselector;
 
 	/* If pin is not described in DTS & enabled, mux_setting is NULL. */
-	setting = pdesc->mux_setting;
 	if (!setting)
 		return -ENOTSUPP;
 	fselector = setting->func;
@@ -365,7 +374,9 @@ static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,
 	struct pcs_device *pcs;
 	struct function_desc *function;
 	struct pcs_function *func;
-	int i;
+	int i, ret;
+	const unsigned *pins = NULL;
+	unsigned num_pins = 0;
 
 	pcs = pinctrl_dev_get_drvdata(pctldev);
 	/* If function mask is null, needn't enable it. */
@@ -376,8 +387,32 @@ static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,
 	if (!func)
 		return -EINVAL;
 
-	dev_dbg(pcs->dev, "enabling %s function%i\n",
-		func->name, fselector);
+	/*
+	 * Last mux setting must be stored here as well.
+	 * Referring to proper pinconf requires the current pin function
+	 * to be known
+	 */
+	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &num_pins);
+	if (ret && PCS_HAS_PINCONF) {
+		dev_err(pcs->dev, "Cannot store mux settings\n");
+		return ret;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		struct pcs_setting_mux *setting;
+		struct pin_desc *desc;
+
+		desc = pin_desc_get(pctldev, pins[i]);
+		if (!desc) {
+			dev_err(pcs->dev, "Unable to request pindesc for PIN%d\n",
+				pins[i]);
+			continue;
+		}
+
+		setting = desc->drv_data;
+		setting->func = fselector;
+		setting->group = group;
+	}
 
 	for (i = 0; i < func->nvals; i++) {
 		struct pcs_func_vals *vals;
@@ -682,6 +717,7 @@ static int pcs_add_pin(struct pcs_device *pcs, unsigned int offset)
 {
 	struct pcs_soc_data *pcs_soc = &pcs->socdata;
 	struct pinctrl_pin_desc *pin;
+	struct pcs_setting_mux *setting;
 	int i;
 
 	i = pcs->pins.cur;
@@ -703,7 +739,14 @@ static int pcs_add_pin(struct pcs_device *pcs, unsigned int offset)
 		}
 	}
 
+
+	setting = devm_kzalloc(pcs->dev, sizeof(*setting), GFP_KERNEL);
+	if (!setting)
+		return -ENOMEM;
+
+
 	pin = &pcs->pins.pa[i];
+	pin->drv_data = setting;
 	pin->number = i;
 	pcs->pins.cur++;

> pin_desc
> > I'm not sure if I miss something important here?
> > Are you aware of this issue? 
> 
> Sounds like something needs to be implemented for pinctrl-single.c.

Ok then I'm not completely wrong. 
I will try to sent out a more complete patchseries for discussions during next two weeks.
  

> 
> Regards,
> 
> Tony

Cheers,
Benedikt




[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux