diff mbox

[net-next,2/2] dsa: mv88e6xxx.c: Hardware reset the chip if available

Message ID 1447889365-25256-3-git-send-email-andrew@lunn.ch
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Andrew Lunn Nov. 18, 2015, 11:29 p.m. UTC
The device tree binding now allows a gpio to be specified which is
attached to the switch chips reset line. If it is defined, perform
a hardware reset on the switch during setup.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/dsa/mv88e6xxx.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

Comments

Florian Fainelli Nov. 18, 2015, 11:51 p.m. UTC | #1
On 18/11/15 15:29, Andrew Lunn wrote:
> The device tree binding now allows a gpio to be specified which is
> attached to the switch chips reset line. If it is defined, perform
> a hardware reset on the switch during setup.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
>  drivers/net/dsa/mv88e6xxx.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
> index b06dba05594a..c0bbbe7713c5 100644
> --- a/drivers/net/dsa/mv88e6xxx.c
> +++ b/drivers/net/dsa/mv88e6xxx.c
> @@ -19,6 +19,7 @@
>  #include <linux/list.h>
>  #include <linux/module.h>
>  #include <linux/netdevice.h>
> +#include <linux/gpio/consumer.h>
>  #include <linux/phy.h>
>  #include <net/dsa.h>
>  #include <net/switchdev.h>
> @@ -2323,7 +2324,10 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
>  {
>  	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
>  	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
> +	int gpio = ds->pd->reset;
> +	int flags = ds->pd->reset_flags;
>  	unsigned long timeout;
> +	int on = 1;
>  	int ret;
>  	int i;
>  
> @@ -2336,6 +2340,16 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
>  	/* Wait for transmit queues to drain. */
>  	usleep_range(2000, 4000);
>  
> +	/* If there is a gpio connected to the reset pin, toggle it */
> +	if (gpio_is_valid(gpio)) {
> +		if (flags && OF_GPIO_ACTIVE_LOW)
> +			on = !on;
> +		gpio_set_value_cansleep(gpio, on);
> +		usleep_range(10000, 20000);
> +		gpio_set_value_cansleep(gpio, !on);
> +		usleep_range(10000, 20000);
> +	}

We are embedding reset logic here about the delays and polarity, while
there is now a proper abstraction for this within the reset controller
subsystem under drivers/reset/core.c. Could we utilize that facility
instead which would make us more robust wrt. non-GPIO reset lines (for
instance some SF2 switches on DSL gateways could definitively benefit
from this).

There does not seem to be a reset controller GPIO binding and generic
driver, but this seems like an appropriate candidate?
Andrew Lunn Nov. 19, 2015, 1:13 a.m. UTC | #2
> We are embedding reset logic here about the delays and polarity, while
> there is now a proper abstraction for this within the reset controller
> subsystem under drivers/reset/core.c. Could we utilize that facility
> instead which would make us more robust wrt. non-GPIO reset lines (for
> instance some SF2 switches on DSL gateways could definitively benefit
> from this).
> 
> There does not seem to be a reset controller GPIO binding and generic
> driver, but this seems like an appropriate candidate?

Hi Florian

That was my first thought as well. But then i went searching and found

http://permalink.gmane.org/gmane.linux.ports.arm.kernel/257149

where Mark Rutland says no to the idea. reset-gpios should be handle
by the device.

Anyway, delays are hard coded, but polarity not. That is in the
generic device tree binding for a GPIO, you can say GPIO_ACTIVE_LOW or
GPIO_ACTIVE_HIGH. The delays are also hard coded in the Marvell
specific part, so should not cause a problem to other manufactures
chips.

	Andrew

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Phil Reid Nov. 19, 2015, 2:08 a.m. UTC | #3
On 19/11/2015 7:29 AM, Andrew Lunn wrote:
> The device tree binding now allows a gpio to be specified which is
> attached to the switch chips reset line. If it is defined, perform
> a hardware reset on the switch during setup.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
>   drivers/net/dsa/mv88e6xxx.c | 14 ++++++++++++++
>   1 file changed, 14 insertions(+)
>
> diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
> index b06dba05594a..c0bbbe7713c5 100644
> --- a/drivers/net/dsa/mv88e6xxx.c
> +++ b/drivers/net/dsa/mv88e6xxx.c
> @@ -19,6 +19,7 @@
>   #include <linux/list.h>
>   #include <linux/module.h>
>   #include <linux/netdevice.h>
> +#include <linux/gpio/consumer.h>
>   #include <linux/phy.h>
>   #include <net/dsa.h>
>   #include <net/switchdev.h>
> @@ -2323,7 +2324,10 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
>   {
>   	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
>   	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
> +	int gpio = ds->pd->reset;
> +	int flags = ds->pd->reset_flags;
>   	unsigned long timeout;
> +	int on = 1;
>   	int ret;
>   	int i;
>
> @@ -2336,6 +2340,16 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
>   	/* Wait for transmit queues to drain. */
>   	usleep_range(2000, 4000);
>
> +	/* If there is a gpio connected to the reset pin, toggle it */
> +	if (gpio_is_valid(gpio)) {
> +		if (flags && OF_GPIO_ACTIVE_LOW)
> +			on = !on;
> +		gpio_set_value_cansleep(gpio, on);
> +		usleep_range(10000, 20000);
> +		gpio_set_value_cansleep(gpio, !on);
> +		usleep_range(10000, 20000);
> +	}
> +
This is a general query about what is the preferred method of allocating gpios.
The gpiod* family of functions provided similar functionality and automatically
deal with active low / high outputs, direction, inital value  etc...
I raise this more for knowledge on what method I should use for my patches.


>   	/* Reset the switch. Keep the PPU active if requested. The PPU
>   	 * needs to be active to support indirect phy register access
>   	 * through global registers 0x18 and 0x19.
>

Other than that the concept looks good and something I has been looking at adding.

Would it be worth considering placing the chip in reset on driver remove?
I have an battery powered hardware platform using one of this marvell devices and
for certain configurations we don't need the switch active. So unloading the
module to place the device in reset and would save power.
Reloading would reinitialise the port.
Andrew Lunn Nov. 19, 2015, 2:25 a.m. UTC | #4
> This is a general query about what is the preferred method of allocating gpios.
> The gpiod* family of functions provided similar functionality and automatically
> deal with active low / high outputs, direction, inital value  etc...
> I raise this more for knowledge on what method I should use for my patches.

I first tried using gpiod, but failed. The API requires that the gpios
be in the root of the device's subtree in the DT blob. But here the
gpios are associated to a switch, and the switch part of the subtree
is one level down. gpiod has no way to get them from there.

> Other than that the concept looks good and something I has been
> looking at adding.

Please feel free to test it on your hardware and send a Tested-by :-)

> Would it be worth considering placing the chip in reset on driver
> remove?  I have an battery powered hardware platform using one of
> this marvell devices and for certain configurations we don't need
> the switch active. So unloading the module to place the device in
> reset and would save power.  Reloading would reinitialise the port.

I think we first need to get module unload/load working reliably.
This is being worked on. But i'm not against this in principle.  Power
saving in general needs working on for Marvall devices. There is no
suspend/resume support for example. It would also be good to ensure
the PHYs are powered off when not needed, etc.

    Andrew

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Phil Reid Nov. 19, 2015, 6:32 a.m. UTC | #5
On 19/11/2015 10:25 AM, Andrew Lunn wrote:
 >> This is a general query about what is the preferred method of allocating gpios.
 >> The gpiod* family of functions provided similar functionality and automatically
 >> deal with active low / high outputs, direction, inital value  etc...
 >> I raise this more for knowledge on what method I should use for my patches.
 >
 > I first tried using gpiod, but failed. The API requires that the gpios
 > be in the root of the device's subtree in the DT blob. But here the
 > gpios are associated to a switch, and the switch part of the subtree
 > is one level down. gpiod has no way to get them from there.
Hadn't dug into the gpiod stuff that far. Yes looks like there limited support for
extracting from sub nodes. There's devm_get_gpiod_from_child which looks like it
does something like that but I don't have any idea how to go from an of_node to
a fwnode_handle.

 > +		gpio = of_get_named_gpio_flags(child, "reset-gpios", 0,
 > +					       &flags);
 > +		if (gpio_is_valid(gpio)) {
 > +			ret = devm_gpio_request_one(dev, gpio, flags,
 > +						    "switch_reset");
 > +			if (ret)
 > +				goto out_free_chip;

The flags passed into devm_gpio_request_one are of type GPIOF_* which don't
match the device tree definitions for flags. As your handling the device flags
manually I think devm_gpio_request_one flags should be 0. Or you can translate
the device tree flags and get devm_gpio_request_one to configure the polarity etc.
Then I think you don't need to do your polarity inversions later on.

 >
 >> Other than that the concept looks good and something I has been
 >> looking at adding.
 >
 > Please feel free to test it on your hardware and send a Tested-by :-)
Worked as expected on my hardware, can see the line toggle, chip is configured correctly.

Tested-by: Phil Reid <preid@electromag.com.au>


 >
 >> Would it be worth considering placing the chip in reset on driver
 >> remove?  I have an battery powered hardware platform using one of
 >> this marvell devices and for certain configurations we don't need
 >> the switch active. So unloading the module to place the device in
 >> reset and would save power.  Reloading would reinitialise the port.
 >
 > I think we first need to get module unload/load working reliably.
 > This is being worked on. But i'm not against this in principle.  Power
 > saving in general needs working on for Marvall devices. There is no
 > suspend/resume support for example. It would also be good to ensure
 > the PHYs are powered off when not needed, etc.
Seems a sound plan.
diff mbox

Patch

diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index b06dba05594a..c0bbbe7713c5 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -19,6 +19,7 @@ 
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
+#include <linux/gpio/consumer.h>
 #include <linux/phy.h>
 #include <net/dsa.h>
 #include <net/switchdev.h>
@@ -2323,7 +2324,10 @@  int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
 {
 	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
+	int gpio = ds->pd->reset;
+	int flags = ds->pd->reset_flags;
 	unsigned long timeout;
+	int on = 1;
 	int ret;
 	int i;
 
@@ -2336,6 +2340,16 @@  int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
 	/* Wait for transmit queues to drain. */
 	usleep_range(2000, 4000);
 
+	/* If there is a gpio connected to the reset pin, toggle it */
+	if (gpio_is_valid(gpio)) {
+		if (flags && OF_GPIO_ACTIVE_LOW)
+			on = !on;
+		gpio_set_value_cansleep(gpio, on);
+		usleep_range(10000, 20000);
+		gpio_set_value_cansleep(gpio, !on);
+		usleep_range(10000, 20000);
+	}
+
 	/* Reset the switch. Keep the PPU active if requested. The PPU
 	 * needs to be active to support indirect phy register access
 	 * through global registers 0x18 and 0x19.