diff options
author | David Daney <david.daney@cavium.com> | 2012-06-27 03:33:35 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-06-28 00:23:24 -0400 |
commit | ac28b9f8cd66d6bc54f8063df59e99abd62173a4 (patch) | |
tree | 9a837eafeead399aebad03fb891654ddeff37513 | |
parent | a3caad0a160c03b7238a2518fa89abda78adef1e (diff) |
netdev/phy: Handle IEEE802.3 clause 45 Ethernet PHYs
The IEEE802.3 clause 45 MDIO bus protocol allows for directly
addressing PHY registers using a 21 bit address, and is used by many
10G Ethernet PHYS. Already existing is the ability of MDIO bus
drivers to use clause 45, with the MII_ADDR_C45 flag. Here we add
struct phy_c45_device_ids to hold the device identifier registers
present in clause 45. struct phy_device gets a couple of new fields:
c45_ids to hold the identifiers and is_c45 to signal that it is clause
45.
get_phy_device() gets a new parameter is_c45 to indicate that the PHY
device should use the clause 45 protocol, and its callers are adjusted
to pass false. The follow-on patch to of_mdio.c will pass true where
appropriate.
EXPORT phy_device_create() so that the follow-on patch to of_mdio.c
can use it to create phy devices for PHYs, that have non-standard
device identifier registers, based on the device tree bindings.
Signed-off-by: David Daney <david.daney@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 105 | ||||
-rw-r--r-- | drivers/of/of_mdio.c | 2 | ||||
-rw-r--r-- | include/linux/phy.h | 18 |
4 files changed, 116 insertions, 11 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 31470b0d0c3..2cee6d218d2 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c | |||
@@ -232,7 +232,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) | |||
232 | struct phy_device *phydev; | 232 | struct phy_device *phydev; |
233 | int err; | 233 | int err; |
234 | 234 | ||
235 | phydev = get_phy_device(bus, addr); | 235 | phydev = get_phy_device(bus, addr, false); |
236 | if (IS_ERR(phydev) || phydev == NULL) | 236 | if (IS_ERR(phydev) || phydev == NULL) |
237 | return phydev; | 237 | return phydev; |
238 | 238 | ||
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 18ab0daf449..ef4cdeebedd 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c | |||
@@ -152,8 +152,8 @@ int phy_scan_fixups(struct phy_device *phydev) | |||
152 | } | 152 | } |
153 | EXPORT_SYMBOL(phy_scan_fixups); | 153 | EXPORT_SYMBOL(phy_scan_fixups); |
154 | 154 | ||
155 | static struct phy_device* phy_device_create(struct mii_bus *bus, | 155 | struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, |
156 | int addr, int phy_id) | 156 | bool is_c45, struct phy_c45_device_ids *c45_ids) |
157 | { | 157 | { |
158 | struct phy_device *dev; | 158 | struct phy_device *dev; |
159 | 159 | ||
@@ -174,8 +174,11 @@ static struct phy_device* phy_device_create(struct mii_bus *bus, | |||
174 | 174 | ||
175 | dev->autoneg = AUTONEG_ENABLE; | 175 | dev->autoneg = AUTONEG_ENABLE; |
176 | 176 | ||
177 | dev->is_c45 = is_c45; | ||
177 | dev->addr = addr; | 178 | dev->addr = addr; |
178 | dev->phy_id = phy_id; | 179 | dev->phy_id = phy_id; |
180 | if (c45_ids) | ||
181 | dev->c45_ids = *c45_ids; | ||
179 | dev->bus = bus; | 182 | dev->bus = bus; |
180 | dev->dev.parent = bus->parent; | 183 | dev->dev.parent = bus->parent; |
181 | dev->dev.bus = &mdio_bus_type; | 184 | dev->dev.bus = &mdio_bus_type; |
@@ -200,20 +203,99 @@ static struct phy_device* phy_device_create(struct mii_bus *bus, | |||
200 | 203 | ||
201 | return dev; | 204 | return dev; |
202 | } | 205 | } |
206 | EXPORT_SYMBOL(phy_device_create); | ||
207 | |||
208 | /** | ||
209 | * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. | ||
210 | * @bus: the target MII bus | ||
211 | * @addr: PHY address on the MII bus | ||
212 | * @phy_id: where to store the ID retrieved. | ||
213 | * @c45_ids: where to store the c45 ID information. | ||
214 | * | ||
215 | * If the PHY devices-in-package appears to be valid, it and the | ||
216 | * corresponding identifiers are stored in @c45_ids, zero is stored | ||
217 | * in @phy_id. Otherwise 0xffffffff is stored in @phy_id. Returns | ||
218 | * zero on success. | ||
219 | * | ||
220 | */ | ||
221 | static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, | ||
222 | struct phy_c45_device_ids *c45_ids) { | ||
223 | int phy_reg; | ||
224 | int i, reg_addr; | ||
225 | const int num_ids = ARRAY_SIZE(c45_ids->device_ids); | ||
226 | |||
227 | /* Find first non-zero Devices In package. Device | ||
228 | * zero is reserved, so don't probe it. | ||
229 | */ | ||
230 | for (i = 1; | ||
231 | i < num_ids && c45_ids->devices_in_package == 0; | ||
232 | i++) { | ||
233 | reg_addr = MII_ADDR_C45 | i << 16 | 6; | ||
234 | phy_reg = mdiobus_read(bus, addr, reg_addr); | ||
235 | if (phy_reg < 0) | ||
236 | return -EIO; | ||
237 | c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; | ||
238 | |||
239 | reg_addr = MII_ADDR_C45 | i << 16 | 5; | ||
240 | phy_reg = mdiobus_read(bus, addr, reg_addr); | ||
241 | if (phy_reg < 0) | ||
242 | return -EIO; | ||
243 | c45_ids->devices_in_package |= (phy_reg & 0xffff); | ||
244 | |||
245 | /* If mostly Fs, there is no device there, | ||
246 | * let's get out of here. | ||
247 | */ | ||
248 | if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { | ||
249 | *phy_id = 0xffffffff; | ||
250 | return 0; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | /* Now probe Device Identifiers for each device present. */ | ||
255 | for (i = 1; i < num_ids; i++) { | ||
256 | if (!(c45_ids->devices_in_package & (1 << i))) | ||
257 | continue; | ||
258 | |||
259 | reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID1; | ||
260 | phy_reg = mdiobus_read(bus, addr, reg_addr); | ||
261 | if (phy_reg < 0) | ||
262 | return -EIO; | ||
263 | c45_ids->device_ids[i] = (phy_reg & 0xffff) << 16; | ||
264 | |||
265 | reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID2; | ||
266 | phy_reg = mdiobus_read(bus, addr, reg_addr); | ||
267 | if (phy_reg < 0) | ||
268 | return -EIO; | ||
269 | c45_ids->device_ids[i] |= (phy_reg & 0xffff); | ||
270 | } | ||
271 | *phy_id = 0; | ||
272 | return 0; | ||
273 | } | ||
203 | 274 | ||
204 | /** | 275 | /** |
205 | * get_phy_id - reads the specified addr for its ID. | 276 | * get_phy_id - reads the specified addr for its ID. |
206 | * @bus: the target MII bus | 277 | * @bus: the target MII bus |
207 | * @addr: PHY address on the MII bus | 278 | * @addr: PHY address on the MII bus |
208 | * @phy_id: where to store the ID retrieved. | 279 | * @phy_id: where to store the ID retrieved. |
280 | * @is_c45: If true the PHY uses the 802.3 clause 45 protocol | ||
281 | * @c45_ids: where to store the c45 ID information. | ||
282 | * | ||
283 | * Description: In the case of a 802.3-c22 PHY, reads the ID registers | ||
284 | * of the PHY at @addr on the @bus, stores it in @phy_id and returns | ||
285 | * zero on success. | ||
286 | * | ||
287 | * In the case of a 802.3-c45 PHY, get_phy_c45_ids() is invoked, and | ||
288 | * its return value is in turn returned. | ||
209 | * | 289 | * |
210 | * Description: Reads the ID registers of the PHY at @addr on the | ||
211 | * @bus, stores it in @phy_id and returns zero on success. | ||
212 | */ | 290 | */ |
213 | static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) | 291 | static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, |
292 | bool is_c45, struct phy_c45_device_ids *c45_ids) | ||
214 | { | 293 | { |
215 | int phy_reg; | 294 | int phy_reg; |
216 | 295 | ||
296 | if (is_c45) | ||
297 | return get_phy_c45_ids(bus, addr, phy_id, c45_ids); | ||
298 | |||
217 | /* Grab the bits from PHYIR1, and put them | 299 | /* Grab the bits from PHYIR1, and put them |
218 | * in the upper half */ | 300 | * in the upper half */ |
219 | phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); | 301 | phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); |
@@ -238,17 +320,19 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) | |||
238 | * get_phy_device - reads the specified PHY device and returns its @phy_device struct | 320 | * get_phy_device - reads the specified PHY device and returns its @phy_device struct |
239 | * @bus: the target MII bus | 321 | * @bus: the target MII bus |
240 | * @addr: PHY address on the MII bus | 322 | * @addr: PHY address on the MII bus |
323 | * @is_c45: If true the PHY uses the 802.3 clause 45 protocol | ||
241 | * | 324 | * |
242 | * Description: Reads the ID registers of the PHY at @addr on the | 325 | * Description: Reads the ID registers of the PHY at @addr on the |
243 | * @bus, then allocates and returns the phy_device to represent it. | 326 | * @bus, then allocates and returns the phy_device to represent it. |
244 | */ | 327 | */ |
245 | struct phy_device * get_phy_device(struct mii_bus *bus, int addr) | 328 | struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) |
246 | { | 329 | { |
247 | struct phy_device *dev = NULL; | 330 | struct phy_device *dev = NULL; |
248 | u32 phy_id; | 331 | u32 phy_id; |
332 | struct phy_c45_device_ids c45_ids = {0}; | ||
249 | int r; | 333 | int r; |
250 | 334 | ||
251 | r = get_phy_id(bus, addr, &phy_id); | 335 | r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); |
252 | if (r) | 336 | if (r) |
253 | return ERR_PTR(r); | 337 | return ERR_PTR(r); |
254 | 338 | ||
@@ -256,7 +340,7 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) | |||
256 | if ((phy_id & 0x1fffffff) == 0x1fffffff) | 340 | if ((phy_id & 0x1fffffff) == 0x1fffffff) |
257 | return NULL; | 341 | return NULL; |
258 | 342 | ||
259 | dev = phy_device_create(bus, addr, phy_id); | 343 | dev = phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); |
260 | 344 | ||
261 | return dev; | 345 | return dev; |
262 | } | 346 | } |
@@ -449,6 +533,11 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | |||
449 | /* Assume that if there is no driver, that it doesn't | 533 | /* Assume that if there is no driver, that it doesn't |
450 | * exist, and we should use the genphy driver. */ | 534 | * exist, and we should use the genphy driver. */ |
451 | if (NULL == d->driver) { | 535 | if (NULL == d->driver) { |
536 | if (phydev->is_c45) { | ||
537 | pr_err("No driver for phy %x\n", phydev->phy_id); | ||
538 | return -ENODEV; | ||
539 | } | ||
540 | |||
452 | d->driver = &genphy_driver.driver; | 541 | d->driver = &genphy_driver.driver; |
453 | 542 | ||
454 | err = d->driver->probe(d); | 543 | err = d->driver->probe(d); |
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 2574abde8d9..6c24cad322d 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c | |||
@@ -79,7 +79,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) | |||
79 | mdio->irq[addr] = PHY_POLL; | 79 | mdio->irq[addr] = PHY_POLL; |
80 | } | 80 | } |
81 | 81 | ||
82 | phy = get_phy_device(mdio, addr); | 82 | phy = get_phy_device(mdio, addr, false); |
83 | if (!phy || IS_ERR(phy)) { | 83 | if (!phy || IS_ERR(phy)) { |
84 | dev_err(&mdio->dev, "error probing PHY at address %i\n", | 84 | dev_err(&mdio->dev, "error probing PHY at address %i\n", |
85 | addr); | 85 | addr); |
diff --git a/include/linux/phy.h b/include/linux/phy.h index c291cae8ce3..597d05dd0fb 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h | |||
@@ -243,6 +243,15 @@ enum phy_state { | |||
243 | PHY_RESUMING | 243 | PHY_RESUMING |
244 | }; | 244 | }; |
245 | 245 | ||
246 | /** | ||
247 | * struct phy_c45_device_ids - 802.3-c45 Device Identifiers | ||
248 | * @devices_in_package: Bit vector of devices present. | ||
249 | * @device_ids: The device identifer for each present device. | ||
250 | */ | ||
251 | struct phy_c45_device_ids { | ||
252 | u32 devices_in_package; | ||
253 | u32 device_ids[8]; | ||
254 | }; | ||
246 | 255 | ||
247 | /* phy_device: An instance of a PHY | 256 | /* phy_device: An instance of a PHY |
248 | * | 257 | * |
@@ -250,6 +259,8 @@ enum phy_state { | |||
250 | * bus: Pointer to the bus this PHY is on | 259 | * bus: Pointer to the bus this PHY is on |
251 | * dev: driver model device structure for this PHY | 260 | * dev: driver model device structure for this PHY |
252 | * phy_id: UID for this device found during discovery | 261 | * phy_id: UID for this device found during discovery |
262 | * c45_ids: 802.3-c45 Device Identifers if is_c45. | ||
263 | * is_c45: Set to true if this phy uses clause 45 addressing. | ||
253 | * state: state of the PHY for management purposes | 264 | * state: state of the PHY for management purposes |
254 | * dev_flags: Device-specific flags used by the PHY driver. | 265 | * dev_flags: Device-specific flags used by the PHY driver. |
255 | * addr: Bus address of PHY | 266 | * addr: Bus address of PHY |
@@ -285,6 +296,9 @@ struct phy_device { | |||
285 | 296 | ||
286 | u32 phy_id; | 297 | u32 phy_id; |
287 | 298 | ||
299 | struct phy_c45_device_ids c45_ids; | ||
300 | bool is_c45; | ||
301 | |||
288 | enum phy_state state; | 302 | enum phy_state state; |
289 | 303 | ||
290 | u32 dev_flags; | 304 | u32 dev_flags; |
@@ -480,7 +494,9 @@ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val) | |||
480 | return mdiobus_write(phydev->bus, phydev->addr, regnum, val); | 494 | return mdiobus_write(phydev->bus, phydev->addr, regnum, val); |
481 | } | 495 | } |
482 | 496 | ||
483 | struct phy_device* get_phy_device(struct mii_bus *bus, int addr); | 497 | struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, |
498 | bool is_c45, struct phy_c45_device_ids *c45_ids); | ||
499 | struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); | ||
484 | int phy_device_register(struct phy_device *phy); | 500 | int phy_device_register(struct phy_device *phy); |
485 | int phy_init_hw(struct phy_device *phydev); | 501 | int phy_init_hw(struct phy_device *phydev); |
486 | struct phy_device * phy_attach(struct net_device *dev, | 502 | struct phy_device * phy_attach(struct net_device *dev, |