OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From 69226896ad636b94f6d2e55d75ff21a29c4de83b Mon Sep 17 00:00:00 2001 |
2 | From: Roger Quadros <rogerq@ti.com> |
||
3 | Date: Fri, 21 Apr 2017 16:15:38 +0300 |
||
4 | Subject: [PATCH] mdio_bus: Issue GPIO RESET to PHYs. |
||
5 | |||
6 | Some boards [1] leave the PHYs at an invalid state |
||
7 | during system power-up or reset thus causing unreliability |
||
8 | issues with the PHY which manifests as PHY not being detected |
||
9 | or link not functional. To fix this, these PHYs need to be RESET |
||
10 | via a GPIO connected to the PHY's RESET pin. |
||
11 | |||
12 | Some boards have a single GPIO controlling the PHY RESET pin of all |
||
13 | PHYs on the bus whereas some others have separate GPIOs controlling |
||
14 | individual PHY RESETs. |
||
15 | |||
16 | In both cases, the RESET de-assertion cannot be done in the PHY driver |
||
17 | as the PHY will not probe till its reset is de-asserted. |
||
18 | So do the RESET de-assertion in the MDIO bus driver. |
||
19 | |||
20 | [1] - am572x-idk, am571x-idk, a437x-idk |
||
21 | |||
22 | Signed-off-by: Roger Quadros <rogerq@ti.com> |
||
23 | Signed-off-by: David S. Miller <davem@davemloft.net> |
||
24 | --- |
||
25 | Documentation/devicetree/bindings/net/mdio.txt | 33 ++++++++++++++++++ |
||
26 | drivers/net/phy/mdio_bus.c | 47 ++++++++++++++++++++++++++ |
||
27 | drivers/of/of_mdio.c | 7 ++++ |
||
28 | include/linux/phy.h | 7 ++++ |
||
29 | 4 files changed, 94 insertions(+) |
||
30 | create mode 100644 Documentation/devicetree/bindings/net/mdio.txt |
||
31 | |||
32 | --- /dev/null |
||
33 | +++ b/Documentation/devicetree/bindings/net/mdio.txt |
||
34 | @@ -0,0 +1,33 @@ |
||
35 | +Common MDIO bus properties. |
||
36 | + |
||
37 | +These are generic properties that can apply to any MDIO bus. |
||
38 | + |
||
39 | +Optional properties: |
||
40 | +- reset-gpios: List of one or more GPIOs that control the RESET lines |
||
41 | + of the PHYs on that MDIO bus. |
||
42 | +- reset-delay-us: RESET pulse width in microseconds as per PHY datasheet. |
||
43 | + |
||
44 | +A list of child nodes, one per device on the bus is expected. These |
||
45 | +should follow the generic phy.txt, or a device specific binding document. |
||
46 | + |
||
47 | +Example : |
||
48 | +This example shows these optional properties, plus other properties |
||
49 | +required for the TI Davinci MDIO driver. |
||
50 | + |
||
51 | + davinci_mdio: ethernet@0x5c030000 { |
||
52 | + compatible = "ti,davinci_mdio"; |
||
53 | + reg = <0x5c030000 0x1000>; |
||
54 | + #address-cells = <1>; |
||
55 | + #size-cells = <0>; |
||
56 | + |
||
57 | + reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; |
||
58 | + reset-delay-us = <2>; /* PHY datasheet states 1us min */ |
||
59 | + |
||
60 | + ethphy0: ethernet-phy@1 { |
||
61 | + reg = <1>; |
||
62 | + }; |
||
63 | + |
||
64 | + ethphy1: ethernet-phy@3 { |
||
65 | + reg = <3>; |
||
66 | + }; |
||
67 | + }; |
||
68 | --- a/drivers/net/phy/mdio_bus.c |
||
69 | +++ b/drivers/net/phy/mdio_bus.c |
||
70 | @@ -22,8 +22,11 @@ |
||
71 | #include <linux/init.h> |
||
72 | #include <linux/delay.h> |
||
73 | #include <linux/device.h> |
||
74 | +#include <linux/gpio.h> |
||
75 | +#include <linux/gpio/consumer.h> |
||
76 | #include <linux/of_device.h> |
||
77 | #include <linux/of_mdio.h> |
||
78 | +#include <linux/of_gpio.h> |
||
79 | #include <linux/netdevice.h> |
||
80 | #include <linux/etherdevice.h> |
||
81 | #include <linux/skbuff.h> |
||
82 | @@ -304,6 +307,7 @@ int __mdiobus_register(struct mii_bus *b |
||
83 | { |
||
84 | struct mdio_device *mdiodev; |
||
85 | int i, err; |
||
86 | + struct gpio_desc *gpiod; |
||
87 | |||
88 | if (NULL == bus || NULL == bus->name || |
||
89 | NULL == bus->read || NULL == bus->write) |
||
90 | @@ -330,6 +334,35 @@ int __mdiobus_register(struct mii_bus *b |
||
91 | if (bus->reset) |
||
92 | bus->reset(bus); |
||
93 | |||
94 | + /* de-assert bus level PHY GPIO resets */ |
||
95 | + if (bus->num_reset_gpios > 0) { |
||
96 | + bus->reset_gpiod = devm_kcalloc(&bus->dev, |
||
97 | + bus->num_reset_gpios, |
||
98 | + sizeof(struct gpio_desc *), |
||
99 | + GFP_KERNEL); |
||
100 | + if (!bus->reset_gpiod) |
||
101 | + return -ENOMEM; |
||
102 | + } |
||
103 | + |
||
104 | + for (i = 0; i < bus->num_reset_gpios; i++) { |
||
105 | + gpiod = devm_gpiod_get_index(&bus->dev, "reset", i, |
||
106 | + GPIOD_OUT_LOW); |
||
107 | + if (IS_ERR(gpiod)) { |
||
108 | + err = PTR_ERR(gpiod); |
||
109 | + if (err != -ENOENT) { |
||
110 | + dev_err(&bus->dev, |
||
111 | + "mii_bus %s couldn't get reset GPIO\n", |
||
112 | + bus->id); |
||
113 | + return err; |
||
114 | + } |
||
115 | + } else { |
||
116 | + bus->reset_gpiod[i] = gpiod; |
||
117 | + gpiod_set_value_cansleep(gpiod, 1); |
||
118 | + udelay(bus->reset_delay_us); |
||
119 | + gpiod_set_value_cansleep(gpiod, 0); |
||
120 | + } |
||
121 | + } |
||
122 | + |
||
123 | for (i = 0; i < PHY_MAX_ADDR; i++) { |
||
124 | if ((bus->phy_mask & (1 << i)) == 0) { |
||
125 | struct phy_device *phydev; |
||
126 | @@ -355,6 +388,13 @@ error: |
||
127 | mdiodev->device_remove(mdiodev); |
||
128 | mdiodev->device_free(mdiodev); |
||
129 | } |
||
130 | + |
||
131 | + /* Put PHYs in RESET to save power */ |
||
132 | + for (i = 0; i < bus->num_reset_gpios; i++) { |
||
133 | + if (bus->reset_gpiod[i]) |
||
134 | + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); |
||
135 | + } |
||
136 | + |
||
137 | device_del(&bus->dev); |
||
138 | return err; |
||
139 | } |
||
140 | @@ -376,6 +416,13 @@ void mdiobus_unregister(struct mii_bus * |
||
141 | mdiodev->device_remove(mdiodev); |
||
142 | mdiodev->device_free(mdiodev); |
||
143 | } |
||
144 | + |
||
145 | + /* Put PHYs in RESET to save power */ |
||
146 | + for (i = 0; i < bus->num_reset_gpios; i++) { |
||
147 | + if (bus->reset_gpiod[i]) |
||
148 | + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); |
||
149 | + } |
||
150 | + |
||
151 | device_del(&bus->dev); |
||
152 | } |
||
153 | EXPORT_SYMBOL(mdiobus_unregister); |
||
154 | --- a/drivers/of/of_mdio.c |
||
155 | +++ b/drivers/of/of_mdio.c |
||
156 | @@ -22,6 +22,8 @@ |
||
157 | #include <linux/of_net.h> |
||
158 | #include <linux/module.h> |
||
159 | |||
160 | +#define DEFAULT_GPIO_RESET_DELAY 10 /* in microseconds */ |
||
161 | + |
||
162 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); |
||
163 | MODULE_LICENSE("GPL"); |
||
164 | |||
165 | @@ -220,6 +222,11 @@ int of_mdiobus_register(struct mii_bus * |
||
166 | |||
167 | mdio->dev.of_node = np; |
||
168 | |||
169 | + /* Get bus level PHY reset GPIO details */ |
||
170 | + mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY; |
||
171 | + of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us); |
||
172 | + mdio->num_reset_gpios = of_gpio_named_count(np, "reset-gpios"); |
||
173 | + |
||
174 | /* Register the MDIO bus */ |
||
175 | rc = mdiobus_register(mdio); |
||
176 | if (rc) |
||
177 | --- a/include/linux/phy.h |
||
178 | +++ b/include/linux/phy.h |
||
179 | @@ -193,6 +193,13 @@ struct mii_bus { |
||
180 | * matching its address |
||
181 | */ |
||
182 | int irq[PHY_MAX_ADDR]; |
||
183 | + |
||
184 | + /* GPIO reset pulse width in microseconds */ |
||
185 | + int reset_delay_us; |
||
186 | + /* Number of reset GPIOs */ |
||
187 | + int num_reset_gpios; |
||
188 | + /* Array of RESET GPIO descriptors */ |
||
189 | + struct gpio_desc **reset_gpiod; |
||
190 | }; |
||
191 | #define to_mii_bus(d) container_of(d, struct mii_bus, dev) |
||
192 |