aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorSean Cross <xobs@kosagi.com>2013-08-20 21:46:12 -0400
committerDavid S. Miller <davem@davemloft.net>2013-08-21 03:03:38 -0400
commit954c396756e3d31985f7bc6a414a988a4736a7d0 (patch)
treee52a1fd45a4e146a6076aad4afd7a04bab6dffef /drivers/net/phy
parent9fd0784164047583fa856c9a5aeda1d6c6fb0399 (diff)
net/phy: micrel: Add OF configuration support for ksz9021
Some boards require custom PHY configuration, for example due to trace length differences. Add the ability to configure these registers in order to get the PHY to function on boards that need it. Because PHYs are auto-detected based on MDIO device IDs, allow PHY configuration to be specified in the parent Ethernet device node if no PHY device node is present. Signed-off-by: Sean Cross <xobs@kosagi.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/micrel.c103
1 files changed, 102 insertions, 1 deletions
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 9ca494550186..c31aad0004cb 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -25,6 +25,7 @@
25#include <linux/module.h> 25#include <linux/module.h>
26#include <linux/phy.h> 26#include <linux/phy.h>
27#include <linux/micrel_phy.h> 27#include <linux/micrel_phy.h>
28#include <linux/of.h>
28 29
29/* Operation Mode Strap Override */ 30/* Operation Mode Strap Override */
30#define MII_KSZPHY_OMSO 0x16 31#define MII_KSZPHY_OMSO 0x16
@@ -53,6 +54,20 @@
53#define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14) 54#define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14)
54#define KSZ8051_RMII_50MHZ_CLK (1 << 7) 55#define KSZ8051_RMII_50MHZ_CLK (1 << 7)
55 56
57/* Write/read to/from extended registers */
58#define MII_KSZPHY_EXTREG 0x0b
59#define KSZPHY_EXTREG_WRITE 0x8000
60
61#define MII_KSZPHY_EXTREG_WRITE 0x0c
62#define MII_KSZPHY_EXTREG_READ 0x0d
63
64/* Extended registers */
65#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104
66#define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105
67#define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106
68
69#define PS_TO_REG 200
70
56static int ksz_config_flags(struct phy_device *phydev) 71static int ksz_config_flags(struct phy_device *phydev)
57{ 72{
58 int regval; 73 int regval;
@@ -65,6 +80,20 @@ static int ksz_config_flags(struct phy_device *phydev)
65 return 0; 80 return 0;
66} 81}
67 82
83static int kszphy_extended_write(struct phy_device *phydev,
84 u32 regnum, u16 val)
85{
86 phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum);
87 return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val);
88}
89
90static int kszphy_extended_read(struct phy_device *phydev,
91 u32 regnum)
92{
93 phy_write(phydev, MII_KSZPHY_EXTREG, regnum);
94 return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
95}
96
68static int kszphy_ack_interrupt(struct phy_device *phydev) 97static int kszphy_ack_interrupt(struct phy_device *phydev)
69{ 98{
70 /* bit[7..0] int status, which is a read and clear register. */ 99 /* bit[7..0] int status, which is a read and clear register. */
@@ -141,6 +170,78 @@ static int ks8051_config_init(struct phy_device *phydev)
141 return rc < 0 ? rc : 0; 170 return rc < 0 ? rc : 0;
142} 171}
143 172
173static int ksz9021_load_values_from_of(struct phy_device *phydev,
174 struct device_node *of_node, u16 reg,
175 char *field1, char *field2,
176 char *field3, char *field4)
177{
178 int val1 = -1;
179 int val2 = -2;
180 int val3 = -3;
181 int val4 = -4;
182 int newval;
183 int matches = 0;
184
185 if (!of_property_read_u32(of_node, field1, &val1))
186 matches++;
187
188 if (!of_property_read_u32(of_node, field2, &val2))
189 matches++;
190
191 if (!of_property_read_u32(of_node, field3, &val3))
192 matches++;
193
194 if (!of_property_read_u32(of_node, field4, &val4))
195 matches++;
196
197 if (!matches)
198 return 0;
199
200 if (matches < 4)
201 newval = kszphy_extended_read(phydev, reg);
202 else
203 newval = 0;
204
205 if (val1 != -1)
206 newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0);
207
208 if (val2 != -1)
209 newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4);
210
211 if (val3 != -1)
212 newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8);
213
214 if (val4 != -1)
215 newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12);
216
217 return kszphy_extended_write(phydev, reg, newval);
218}
219
220static int ksz9021_config_init(struct phy_device *phydev)
221{
222 struct device *dev = &phydev->dev;
223 struct device_node *of_node = dev->of_node;
224
225 if (!of_node && dev->parent->of_node)
226 of_node = dev->parent->of_node;
227
228 if (of_node) {
229 ksz9021_load_values_from_of(phydev, of_node,
230 MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
231 "txen-skew-ps", "txc-skew-ps",
232 "rxdv-skew-ps", "rxc-skew-ps");
233 ksz9021_load_values_from_of(phydev, of_node,
234 MII_KSZPHY_RX_DATA_PAD_SKEW,
235 "rxd0-skew-ps", "rxd1-skew-ps",
236 "rxd2-skew-ps", "rxd3-skew-ps");
237 ksz9021_load_values_from_of(phydev, of_node,
238 MII_KSZPHY_TX_DATA_PAD_SKEW,
239 "txd0-skew-ps", "txd1-skew-ps",
240 "txd2-skew-ps", "txd3-skew-ps");
241 }
242 return 0;
243}
244
144#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 245#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06
145#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6) 246#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6)
146#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4) 247#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4)
@@ -281,7 +382,7 @@ static struct phy_driver ksphy_driver[] = {
281 .name = "Micrel KSZ9021 Gigabit PHY", 382 .name = "Micrel KSZ9021 Gigabit PHY",
282 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), 383 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
283 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 384 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
284 .config_init = kszphy_config_init, 385 .config_init = ksz9021_config_init,
285 .config_aneg = genphy_config_aneg, 386 .config_aneg = genphy_config_aneg,
286 .read_status = genphy_read_status, 387 .read_status = genphy_read_status,
287 .ack_interrupt = kszphy_ack_interrupt, 388 .ack_interrupt = kszphy_ack_interrupt,