| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | /* Framework for finding and configuring PHYs.
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * Also contains generic PHY driver | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: Andy Fleming | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2004 Freescale Semiconductor, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute  it and/or modify it | 
					
						
							|  |  |  |  * under  the terms of  the GNU General  Public License as published by the | 
					
						
							|  |  |  |  * Free Software Foundation;  either version 2 of the  License, or (at your | 
					
						
							|  |  |  |  * option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-06-09 07:49:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/string.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/unistd.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/netdevice.h>
 | 
					
						
							|  |  |  | #include <linux/etherdevice.h>
 | 
					
						
							|  |  |  | #include <linux/skbuff.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/mii.h>
 | 
					
						
							|  |  |  | #include <linux/ethtool.h>
 | 
					
						
							|  |  |  | #include <linux/phy.h>
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/uaccess.h>
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <asm/irq.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-12-14 00:33:49 +01:00
										 |  |  | MODULE_DESCRIPTION("PHY library"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Andy Fleming"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-12-04 16:17:33 +03:00
										 |  |  | void phy_device_free(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-28 01:01:52 +00:00
										 |  |  | 	put_device(&phydev->dev); | 
					
						
							| 
									
										
										
										
											2007-12-04 16:17:33 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | EXPORT_SYMBOL(phy_device_free); | 
					
						
							| 
									
										
										
										
											2007-12-04 16:17:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void phy_device_release(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-28 01:01:52 +00:00
										 |  |  | 	kfree(to_phy_device(dev)); | 
					
						
							| 
									
										
										
										
											2007-12-04 16:17:33 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | static struct phy_driver genphy_driver; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | static LIST_HEAD(phy_fixup_list); | 
					
						
							|  |  |  | static DEFINE_MUTEX(phy_fixup_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-21 08:37:41 +00:00
										 |  |  | static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | 
					
						
							|  |  |  | 			     u32 flags, phy_interface_t interface); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_register_fixup - creates a new phy_fixup and adds it to the list | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  |  * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) | 
					
						
							|  |  |  |  * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  |  *	It can also be PHY_ANY_UID | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  |  * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  |  *	comparison | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  |  * @run: The actual code to be run when a matching PHY is found | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 		       int (*run)(struct phy_device *)) | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-01-05 03:23:19 +03:00
										 |  |  | 	struct phy_fixup *fixup = kzalloc(sizeof(*fixup), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!fixup) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-10 13:55:14 -08:00
										 |  |  | 	strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id)); | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 	fixup->phy_uid = phy_uid; | 
					
						
							|  |  |  | 	fixup->phy_uid_mask = phy_uid_mask; | 
					
						
							|  |  |  | 	fixup->run = run; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&phy_fixup_lock); | 
					
						
							|  |  |  | 	list_add_tail(&fixup->list, &phy_fixup_list); | 
					
						
							|  |  |  | 	mutex_unlock(&phy_fixup_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_register_fixup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Registers a fixup to be run on any PHY with the UID in phy_uid */ | 
					
						
							|  |  |  | int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 			       int (*run)(struct phy_device *)) | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_register_fixup_for_uid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Registers a fixup to be run on the PHY with id string bus_id */ | 
					
						
							|  |  |  | int phy_register_fixup_for_id(const char *bus_id, | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 			      int (*run)(struct phy_device *)) | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_register_fixup_for_id); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | /* Returns 1 if fixup matches phydev in bus_id and phy_uid.
 | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  |  * Fixups can be set to match any in one or more fields. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-10 13:55:14 -08:00
										 |  |  | 	if (strcmp(fixup->bus_id, dev_name(&phydev->dev)) != 0) | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 		if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((fixup->phy_uid & fixup->phy_uid_mask) != | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	    (phydev->phy_id & fixup->phy_uid_mask)) | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 		if (fixup->phy_uid != PHY_ANY_UID) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Runs any matching fixups for this phydev */ | 
					
						
							| 
									
										
										
										
											2014-01-05 03:28:27 +03:00
										 |  |  | static int phy_scan_fixups(struct phy_device *phydev) | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct phy_fixup *fixup; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&phy_fixup_lock); | 
					
						
							|  |  |  | 	list_for_each_entry(fixup, &phy_fixup_list, list) { | 
					
						
							|  |  |  | 		if (phy_needs_fixup(phydev, fixup)) { | 
					
						
							| 
									
										
										
										
											2014-01-05 03:23:19 +03:00
										 |  |  | 			int err = fixup->run(phydev); | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-13 11:23:39 +00:00
										 |  |  | 			if (err < 0) { | 
					
						
							|  |  |  | 				mutex_unlock(&phy_fixup_lock); | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 				return err; | 
					
						
							| 
									
										
										
										
											2009-07-13 11:23:39 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-04-18 17:29:54 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(&phy_fixup_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 				     bool is_c45, | 
					
						
							|  |  |  | 				     struct phy_c45_device_ids *c45_ids) | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct phy_device *dev; | 
					
						
							| 
									
										
										
										
											2010-04-02 01:05:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	/* We allocate the device, and initialize the default values */ | 
					
						
							| 
									
										
										
										
											2006-12-13 00:34:52 -08:00
										 |  |  | 	dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 	if (NULL == dev) | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | 		return (struct phy_device *)PTR_ERR((void *)-ENOMEM); | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-12-04 16:17:33 +03:00
										 |  |  | 	dev->dev.release = phy_device_release; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 	dev->speed = 0; | 
					
						
							|  |  |  | 	dev->duplex = -1; | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	dev->pause = 0; | 
					
						
							|  |  |  | 	dev->asym_pause = 0; | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 	dev->link = 1; | 
					
						
							| 
									
										
										
										
											2006-12-01 12:01:06 -06:00
										 |  |  | 	dev->interface = PHY_INTERFACE_MODE_GMII; | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dev->autoneg = AUTONEG_ENABLE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | 	dev->is_c45 = is_c45; | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 	dev->addr = addr; | 
					
						
							|  |  |  | 	dev->phy_id = phy_id; | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | 	if (c45_ids) | 
					
						
							|  |  |  | 		dev->c45_ids = *c45_ids; | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 	dev->bus = bus; | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | 	dev->dev.parent = bus->parent; | 
					
						
							|  |  |  | 	dev->dev.bus = &mdio_bus_type; | 
					
						
							|  |  |  | 	dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; | 
					
						
							|  |  |  | 	dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dev->state = PHY_DOWN; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-29 10:05:09 -06:00
										 |  |  | 	mutex_init(&dev->lock); | 
					
						
							| 
									
										
										
										
											2010-01-18 05:37:16 +00:00
										 |  |  | 	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); | 
					
						
							| 
									
										
										
										
											2013-05-19 22:53:43 +00:00
										 |  |  | 	INIT_WORK(&dev->phy_queue, phy_change); | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-02 01:05:27 +00:00
										 |  |  | 	/* Request the appropriate module unconditionally; don't
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * bother trying to do so only if it isn't already loaded, | 
					
						
							|  |  |  | 	 * because that gets complicated. A hotplug event would have | 
					
						
							|  |  |  | 	 * done an unconditional modprobe anyway. | 
					
						
							|  |  |  | 	 * We don't do normal hotplug because it won't work for MDIO | 
					
						
							|  |  |  | 	 * -- because it relies on the device staying around for long | 
					
						
							|  |  |  | 	 * enough for the driver to get loaded. With MDIO, the NIC | 
					
						
							|  |  |  | 	 * driver will get bored and give up as soon as it finds that | 
					
						
							|  |  |  | 	 * there's no driver _already_ loaded. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2010-04-02 01:05:27 +00:00
										 |  |  | 	request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 01:01:52 +00:00
										 |  |  | 	device_initialize(&dev->dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 	return dev; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | EXPORT_SYMBOL(phy_device_create); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. | 
					
						
							|  |  |  |  * @bus: the target MII bus | 
					
						
							|  |  |  |  * @addr: PHY address on the MII bus | 
					
						
							|  |  |  |  * @phy_id: where to store the ID retrieved. | 
					
						
							|  |  |  |  * @c45_ids: where to store the c45 ID information. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   If the PHY devices-in-package appears to be valid, it and the | 
					
						
							|  |  |  |  *   corresponding identifiers are stored in @c45_ids, zero is stored | 
					
						
							|  |  |  |  *   in @phy_id.  Otherwise 0xffffffff is stored in @phy_id.  Returns | 
					
						
							|  |  |  |  *   zero on success. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, | 
					
						
							|  |  |  | 			   struct phy_c45_device_ids *c45_ids) { | 
					
						
							|  |  |  | 	int phy_reg; | 
					
						
							|  |  |  | 	int i, reg_addr; | 
					
						
							|  |  |  | 	const int num_ids = ARRAY_SIZE(c45_ids->device_ids); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Find first non-zero Devices In package.  Device
 | 
					
						
							|  |  |  | 	 * zero is reserved, so don't probe it. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	for (i = 1; | 
					
						
							|  |  |  | 	     i < num_ids && c45_ids->devices_in_package == 0; | 
					
						
							|  |  |  | 	     i++) { | 
					
						
							|  |  |  | 		reg_addr = MII_ADDR_C45 | i << 16 | 6; | 
					
						
							|  |  |  | 		phy_reg = mdiobus_read(bus, addr, reg_addr); | 
					
						
							|  |  |  | 		if (phy_reg < 0) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg_addr = MII_ADDR_C45 | i << 16 | 5; | 
					
						
							|  |  |  | 		phy_reg = mdiobus_read(bus, addr, reg_addr); | 
					
						
							|  |  |  | 		if (phy_reg < 0) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		c45_ids->devices_in_package |= (phy_reg & 0xffff); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* If mostly Fs, there is no device there,
 | 
					
						
							|  |  |  | 		 * let's get out of here. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { | 
					
						
							|  |  |  | 			*phy_id = 0xffffffff; | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Now probe Device Identifiers for each device present. */ | 
					
						
							|  |  |  | 	for (i = 1; i < num_ids; i++) { | 
					
						
							|  |  |  | 		if (!(c45_ids->devices_in_package & (1 << i))) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID1; | 
					
						
							|  |  |  | 		phy_reg = mdiobus_read(bus, addr, reg_addr); | 
					
						
							|  |  |  | 		if (phy_reg < 0) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		c45_ids->device_ids[i] = (phy_reg & 0xffff) << 16; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID2; | 
					
						
							|  |  |  | 		phy_reg = mdiobus_read(bus, addr, reg_addr); | 
					
						
							|  |  |  | 		if (phy_reg < 0) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		c45_ids->device_ids[i] |= (phy_reg & 0xffff); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*phy_id = 0; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-08-14 23:00:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  |  * get_phy_id - reads the specified addr for its ID. | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * @bus: the target MII bus | 
					
						
							|  |  |  |  * @addr: PHY address on the MII bus | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  |  * @phy_id: where to store the ID retrieved. | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  |  * @is_c45: If true the PHY uses the 802.3 clause 45 protocol | 
					
						
							|  |  |  |  * @c45_ids: where to store the c45 ID information. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Description: In the case of a 802.3-c22 PHY, reads the ID registers | 
					
						
							|  |  |  |  *   of the PHY at @addr on the @bus, stores it in @phy_id and returns | 
					
						
							|  |  |  |  *   zero on success. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   In the case of a 802.3-c45 PHY, get_phy_c45_ids() is invoked, and | 
					
						
							|  |  |  |  *   its return value is in turn returned. | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, | 
					
						
							|  |  |  | 		      bool is_c45, struct phy_c45_device_ids *c45_ids) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	int phy_reg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | 	if (is_c45) | 
					
						
							|  |  |  | 		return get_phy_c45_ids(bus, addr, phy_id, c45_ids); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	/* Grab the bits from PHYIR1, and put them in the upper half */ | 
					
						
							| 
									
										
										
										
											2011-09-30 11:51:22 +00:00
										 |  |  | 	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	if (phy_reg < 0) | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  | 		return -EIO; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  | 	*phy_id = (phy_reg & 0xffff) << 16; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Grab the bits from PHYIR2, and put them in the lower half */ | 
					
						
							| 
									
										
										
										
											2011-09-30 11:51:22 +00:00
										 |  |  | 	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	if (phy_reg < 0) | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  | 		return -EIO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*phy_id |= (phy_reg & 0xffff); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  |  * get_phy_device - reads the specified PHY device and returns its @phy_device | 
					
						
							|  |  |  |  *		    struct | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  |  * @bus: the target MII bus | 
					
						
							|  |  |  |  * @addr: PHY address on the MII bus | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  |  * @is_c45: If true the PHY uses the 802.3 clause 45 protocol | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Description: Reads the ID registers of the PHY at @addr on the | 
					
						
							|  |  |  |  *   @bus, then allocates and returns the phy_device to represent it. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | 	struct phy_c45_device_ids c45_ids = {0}; | 
					
						
							| 
									
										
										
										
											2012-06-27 21:28:14 -07:00
										 |  |  | 	u32 phy_id = 0; | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  | 	int r; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | 	r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); | 
					
						
							| 
									
										
										
										
											2008-04-15 12:49:21 -04:00
										 |  |  | 	if (r) | 
					
						
							|  |  |  | 		return ERR_PTR(r); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-20 20:43:18 -08:00
										 |  |  | 	/* If the phy_id is mostly Fs, there is no device there */ | 
					
						
							|  |  |  | 	if ((phy_id & 0x1fffffff) == 0x1fffffff) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:21:52 +03:00
										 |  |  | 	return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | EXPORT_SYMBOL(get_phy_device); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * phy_device_register - Register the phy device on the MDIO bus | 
					
						
							| 
									
										
										
										
											2009-06-16 16:56:33 +00:00
										 |  |  |  * @phydev: phy_device structure to be added to the MDIO bus | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | int phy_device_register(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	/* Don't register a phy if one is already registered at this address */ | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | 	if (phydev->bus->phy_map[phydev->addr]) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	phydev->bus->phy_map[phydev->addr] = phydev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Run all of the fixups for this PHY */ | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:34 -08:00
										 |  |  | 	err = phy_init_hw(phydev); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		pr_err("PHY %d failed to initialize\n", phydev->addr); | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 01:01:52 +00:00
										 |  |  | 	err = device_add(&phydev->dev); | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | 	if (err) { | 
					
						
							| 
									
										
										
										
											2013-02-28 01:01:52 +00:00
										 |  |  | 		pr_err("PHY %d failed to add\n", phydev->addr); | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:46 +00:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  out: | 
					
						
							|  |  |  | 	phydev->bus->phy_map[phydev->addr] = NULL; | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_device_register); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-04 10:23:02 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_find_first - finds the first PHY device on the bus | 
					
						
							|  |  |  |  * @bus: the target MII bus | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct phy_device *phy_find_first(struct mii_bus *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int addr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (addr = 0; addr < PHY_MAX_ADDR; addr++) { | 
					
						
							|  |  |  | 		if (bus->phy_map[addr]) | 
					
						
							|  |  |  | 			return bus->phy_map[addr]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_find_first); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_prepare_link - prepares the PHY layer to monitor link status | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							|  |  |  |  * @handler: callback function for link status change notifications | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Tells the PHY infrastructure to handle the | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   gory details on monitoring link status (whether through | 
					
						
							|  |  |  |  *   polling or an interrupt), and to call back to the | 
					
						
							|  |  |  |  *   connected device driver when the link status changes. | 
					
						
							|  |  |  |  *   If you want to monitor your own link state, don't call | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  *   this function. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-10-21 08:37:41 +00:00
										 |  |  | static void phy_prepare_link(struct phy_device *phydev, | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 			     void (*handler)(struct net_device *)) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	phydev->adjust_link = handler; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_connect_direct - connect an ethernet device to a specific phy_device | 
					
						
							|  |  |  |  * @dev: the network device to connect | 
					
						
							|  |  |  |  * @phydev: the pointer to the phy device | 
					
						
							|  |  |  |  * @handler: callback function for state change notifications | 
					
						
							|  |  |  |  * @interface: PHY device's interface | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, | 
					
						
							| 
									
										
										
										
											2013-01-14 00:52:52 +00:00
										 |  |  | 		       void (*handler)(struct net_device *), | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 		       phy_interface_t interface) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-14 00:52:52 +00:00
										 |  |  | 	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	if (rc) | 
					
						
							|  |  |  | 		return rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	phy_prepare_link(phydev, handler); | 
					
						
							| 
									
										
										
										
											2014-01-05 03:27:17 +03:00
										 |  |  | 	phy_start_machine(phydev); | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	if (phydev->irq > 0) | 
					
						
							|  |  |  | 		phy_start_interrupts(phydev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_connect_direct); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_connect - connect an ethernet device to a PHY device | 
					
						
							|  |  |  |  * @dev: the network device to connect | 
					
						
							| 
									
										
										
										
											2008-04-28 10:58:22 -07:00
										 |  |  |  * @bus_id: the id string of the PHY device to connect | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * @handler: callback function for state change notifications | 
					
						
							|  |  |  |  * @interface: PHY device's interface | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Convenience function for connecting ethernet | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  |  *   devices to PHY devices.  The default behavior is for | 
					
						
							|  |  |  |  *   the PHY infrastructure to handle everything, and only notify | 
					
						
							|  |  |  |  *   the connected driver when the link status changes.  If you | 
					
						
							|  |  |  |  *   don't want, or can't use the provided functionality, you may | 
					
						
							|  |  |  |  *   choose to call only the subset of functions which provide | 
					
						
							|  |  |  |  *   the desired functionality. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | struct phy_device *phy_connect(struct net_device *dev, const char *bus_id, | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 			       void (*handler)(struct net_device *), | 
					
						
							|  |  |  | 			       phy_interface_t interface) | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct phy_device *phydev; | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	struct device *d; | 
					
						
							|  |  |  | 	int rc; | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	/* Search the list of PHY devices on the mdio bus for the
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * PHY with the requested name | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id); | 
					
						
							|  |  |  | 	if (!d) { | 
					
						
							|  |  |  | 		pr_err("PHY %s not found\n", bus_id); | 
					
						
							|  |  |  | 		return ERR_PTR(-ENODEV); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	phydev = to_phy_device(d); | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-14 00:52:52 +00:00
										 |  |  | 	rc = phy_connect_direct(dev, phydev, handler, interface); | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	if (rc) | 
					
						
							|  |  |  | 		return ERR_PTR(rc); | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return phydev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_connect); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  |  * phy_disconnect - disable interrupts, stop state machine, and detach a PHY | 
					
						
							|  |  |  |  *		    device | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * @phydev: target phy_device struct | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | void phy_disconnect(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (phydev->irq > 0) | 
					
						
							|  |  |  | 		phy_stop_interrupts(phydev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	phy_stop_machine(phydev); | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	phydev->adjust_link = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	phy_detach(phydev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_disconnect); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:34 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_poll_reset - Safely wait until a PHY reset has properly completed | 
					
						
							|  |  |  |  * @phydev: The PHY device to poll | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as | 
					
						
							|  |  |  |  *   published in 2008, a PHY reset may take up to 0.5 seconds.  The MII BMCR | 
					
						
							|  |  |  |  *   register must be polled until the BMCR_RESET bit clears. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   Furthermore, any attempts to write to PHY registers may have no effect | 
					
						
							|  |  |  |  *   or even generate MDIO bus errors until this is complete. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   Some PHYs (such as the Marvell 88E1111) don't entirely conform to the | 
					
						
							|  |  |  |  *   standard and do not fully reset after the BMCR_RESET bit is set, and may | 
					
						
							|  |  |  |  *   even *REQUIRE* a soft-reset to properly restart autonegotiation.  In an | 
					
						
							|  |  |  |  *   effort to support such broken PHYs, this function is separate from the | 
					
						
							|  |  |  |  *   standard phy_init_hw() which will zero all the other bits in the BMCR | 
					
						
							|  |  |  |  *   and reapply all driver-specific and board-specific fixups. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int phy_poll_reset(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ | 
					
						
							|  |  |  | 	unsigned int retries = 12; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		msleep(50); | 
					
						
							|  |  |  | 		ret = phy_read(phydev, MII_BMCR); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 	} while (ret & BMCR_RESET && --retries); | 
					
						
							|  |  |  | 	if (ret & BMCR_RESET) | 
					
						
							|  |  |  | 		return -ETIMEDOUT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	/* Some chips (smsc911x) may still need up to another 1ms after the
 | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:34 -08:00
										 |  |  | 	 * BMCR_RESET bit is cleared before they are usable. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	msleep(1); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-30 08:23:30 +00:00
										 |  |  | int phy_init_hw(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!phydev->drv || !phydev->drv->config_init) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:34 -08:00
										 |  |  | 	ret = phy_write(phydev, MII_BMCR, BMCR_RESET); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = phy_poll_reset(phydev); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-30 08:23:30 +00:00
										 |  |  | 	ret = phy_scan_fixups(phydev); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return phydev->drv->config_init(phydev); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:34 -08:00
										 |  |  | EXPORT_SYMBOL(phy_init_hw); | 
					
						
							| 
									
										
										
										
											2009-12-30 08:23:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  |  * phy_attach_direct - attach a network device to a given PHY device pointer | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * @dev: network device to attach | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  |  * @phydev: Pointer to phy_device to attach | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * @flags: PHY device's dev_flags | 
					
						
							|  |  |  |  * @interface: PHY device's interface | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Called by drivers to attach to a particular PHY | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  |  *     device. The phy_device is found, and properly hooked up | 
					
						
							|  |  |  |  *     to the phy_driver.  If no driver is attached, then the | 
					
						
							|  |  |  |  *     genphy_driver is used.  The phy_device is given a ptr to | 
					
						
							|  |  |  |  *     the attaching device, and given a callback for link status | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  *     change.  The phy_device is returned to the attaching driver. | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-10-21 08:37:41 +00:00
										 |  |  | static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | 
					
						
							|  |  |  | 			     u32 flags, phy_interface_t interface) | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	struct device *d = &phydev->dev; | 
					
						
							| 
									
										
										
										
											2011-03-28 14:54:08 +00:00
										 |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Assume that if there is no driver, that it doesn't
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * exist, and we should use the genphy driver. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	if (NULL == d->driver) { | 
					
						
							| 
									
										
										
										
											2012-06-27 07:33:35 +00:00
										 |  |  | 		if (phydev->is_c45) { | 
					
						
							|  |  |  | 			pr_err("No driver for phy %x\n", phydev->phy_id); | 
					
						
							|  |  |  | 			return -ENODEV; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 		d->driver = &genphy_driver.driver; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = d->driver->probe(d); | 
					
						
							| 
									
										
										
										
											2006-10-01 07:27:46 -04:00
										 |  |  | 		if (err >= 0) | 
					
						
							|  |  |  | 			err = device_bind_driver(d); | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-01 07:27:46 -04:00
										 |  |  | 		if (err) | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 			return err; | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (phydev->attached_dev) { | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 		dev_err(&dev->dev, "PHY already attached\n"); | 
					
						
							|  |  |  | 		return -EBUSY; | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	phydev->attached_dev = dev; | 
					
						
							| 
									
										
										
										
											2010-07-17 08:49:36 +00:00
										 |  |  | 	dev->phydev = phydev; | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	phydev->dev_flags = flags; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-01 12:01:06 -06:00
										 |  |  | 	phydev->interface = interface; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												phylib: Fix race between returning phydev and calling adjust_link
It is possible that phylib will call adjust_link before returning
from {,of_}phy_connect(), which may cause the following [very rare,
though] oops upon reopening the device:
  Unable to handle kernel paging request for data at address 0x0000024c
  Oops: Kernel access of bad area, sig: 11 [#1]
  PREEMPT SMP NR_CPUS=2 LTT NESTING LEVEL : 0
  P1021 RDB
  Modules linked in:
  NIP: c0345dac LR: c0345dac CTR: c0345d84
  TASK = dffab6b0[30] 'events/0' THREAD: c0d24000 CPU: 0
  [...]
  NIP [c0345dac] adjust_link+0x28/0x19c
  LR [c0345dac] adjust_link+0x28/0x19c
  Call Trace:
  [c0d25f00] [000045e1] 0x45e1 (unreliable)
  [c0d25f30] [c036c158] phy_state_machine+0x3ac/0x554
  [...]
Here is why. Drivers store phydev in their private structures, e.g.
gianfar driver:
static int init_phy(struct net_device *dev)
{
	...
	priv->phydev = of_phy_connect(...);
	...
}
So that adjust_link could retrieve it back:
static void adjust_link(struct net_device *dev)
{
	...
	struct phy_device *phydev = priv->phydev;
	...
}
If the device has been opened before, then phydev->state is set to
PHY_HALTED (or undefined if the driver didn't call phy_stop()).
Now, phy_connect starts the PHY state machine before returning phydev to
the driver:
	phy_start_machine(phydev, NULL);
	if (phydev->irq > 0)
		phy_start_interrupts(phydev);
	return phydev;
The time between 'phy_start_machine()' and 'return phydev' is undefined.
The start machine routine delays execution for 1 second, which is enough
for most cases. But under heavy load, or if you're unlucky, it is quite
possible that PHY state machine will execute before phy_connect()
returns, and so adjust_link callback will try to dereference phydev,
which is not yet ready.
To fix the issue, simply initialize the PHY's state to PHY_READY during
phy_attach(). This will ensure that phylib won't call adjust_link before
phy_start().
Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
											
										 
											2010-08-24 14:46:12 -07:00
										 |  |  | 	phydev->state = PHY_READY; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-01 12:01:06 -06:00
										 |  |  | 	/* Do initial configuration here, now that
 | 
					
						
							|  |  |  | 	 * we have certain key parameters | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * (dev_flags and interface) | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2011-03-28 14:54:08 +00:00
										 |  |  | 	err = phy_init_hw(phydev); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		phy_detach(phydev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-13 10:20:28 +01:00
										 |  |  | 	phy_resume(phydev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-28 14:54:08 +00:00
										 |  |  | 	return err; | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * phy_attach - attach a network device to a particular PHY device | 
					
						
							|  |  |  |  * @dev: network device to attach | 
					
						
							|  |  |  |  * @bus_id: Bus ID of PHY device to attach | 
					
						
							|  |  |  |  * @interface: PHY device's interface | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Description: Same as phy_attach_direct() except that a PHY bus_id | 
					
						
							|  |  |  |  *     string is passed instead of a pointer to a struct phy_device. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, | 
					
						
							|  |  |  | 			      phy_interface_t interface) | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct bus_type *bus = &mdio_bus_type; | 
					
						
							|  |  |  | 	struct phy_device *phydev; | 
					
						
							|  |  |  | 	struct device *d; | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Search the list of PHY devices on the mdio bus for the
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * PHY with the requested name | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	d = bus_find_device_by_name(bus, NULL, bus_id); | 
					
						
							|  |  |  | 	if (!d) { | 
					
						
							|  |  |  | 		pr_err("PHY %s not found\n", bus_id); | 
					
						
							|  |  |  | 		return ERR_PTR(-ENODEV); | 
					
						
							| 
									
										
										
										
											2006-12-01 12:01:06 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	phydev = to_phy_device(d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-14 00:52:52 +00:00
										 |  |  | 	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); | 
					
						
							| 
									
										
										
										
											2009-04-25 12:52:51 +00:00
										 |  |  | 	if (rc) | 
					
						
							|  |  |  | 		return ERR_PTR(rc); | 
					
						
							| 
									
										
										
										
											2006-12-01 12:01:06 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	return phydev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_attach); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_detach - detach a PHY device from its network device | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | void phy_detach(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-07-17 08:49:36 +00:00
										 |  |  | 	phydev->attached_dev->phydev = NULL; | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	phydev->attached_dev = NULL; | 
					
						
							| 
									
										
										
										
											2013-12-13 10:20:28 +01:00
										 |  |  | 	phy_suspend(phydev); | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If the device had no specific driver before (i.e. - it
 | 
					
						
							|  |  |  | 	 * was using the generic driver), we unbind the device | 
					
						
							|  |  |  | 	 * from the generic driver so that there's a chance a | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * real driver could be loaded | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-04-09 11:52:31 -04:00
										 |  |  | 	if (phydev->dev.driver == &genphy_driver.driver) | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 		device_release_driver(&phydev->dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_detach); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-13 10:20:27 +01:00
										 |  |  | int phy_suspend(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); | 
					
						
							|  |  |  | 	struct ethtool_wolinfo wol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If the device has WOL enabled, we cannot suspend the PHY */ | 
					
						
							|  |  |  | 	wol.cmd = ETHTOOL_GWOL; | 
					
						
							|  |  |  | 	phy_ethtool_get_wol(phydev, &wol); | 
					
						
							|  |  |  | 	if (wol.wolopts) | 
					
						
							|  |  |  | 		return -EBUSY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (phydrv->suspend) | 
					
						
							|  |  |  | 		return phydrv->suspend(phydev); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int phy_resume(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (phydrv->resume) | 
					
						
							|  |  |  | 		return phydrv->resume(phydev); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | /* Generic PHY support and helper functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2011-03-30 22:57:33 -03:00
										 |  |  |  * genphy_config_advert - sanitize and advertise auto-negotiation parameters | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * @phydev: target phy_device struct | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Writes MII_ADVERTISE with the appropriate values, | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   after sanitizing the values to make sure we only advertise | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  |  *   what is supported.  Returns < 0 on error, 0 if the PHY's advertisement | 
					
						
							|  |  |  |  *   hasn't changed, and > 0 if it has changed. | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-10-21 08:37:41 +00:00
										 |  |  | static int genphy_config_advert(struct phy_device *phydev) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	u32 advertise; | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  | 	int oldadv, adv; | 
					
						
							|  |  |  | 	int err, changed = 0; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	/* Only allow advertising what this PHY supports */ | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	phydev->advertising &= phydev->supported; | 
					
						
							|  |  |  | 	advertise = phydev->advertising; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Setup standard advertisement */ | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	adv = phy_read(phydev, MII_ADVERTISE); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	if (adv < 0) | 
					
						
							|  |  |  | 		return adv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	oldadv = adv; | 
					
						
							| 
									
										
										
										
											2011-11-16 18:36:59 -05:00
										 |  |  | 	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 		 ADVERTISE_PAUSE_ASYM); | 
					
						
							| 
									
										
										
										
											2011-11-17 14:30:55 +00:00
										 |  |  | 	adv |= ethtool_adv_to_mii_adv_t(advertise); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  | 	if (adv != oldadv) { | 
					
						
							|  |  |  | 		err = phy_write(phydev, MII_ADVERTISE, adv); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		changed = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Configure gigabit if it's supported */ | 
					
						
							|  |  |  | 	if (phydev->supported & (SUPPORTED_1000baseT_Half | | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 				 SUPPORTED_1000baseT_Full)) { | 
					
						
							|  |  |  | 		adv = phy_read(phydev, MII_CTRL1000); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 		if (adv < 0) | 
					
						
							|  |  |  | 			return adv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 		oldadv = adv; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 		adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); | 
					
						
							| 
									
										
										
										
											2011-11-17 14:30:55 +00:00
										 |  |  | 		adv |= ethtool_adv_to_mii_ctrl1000_t(advertise); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  | 		if (adv != oldadv) { | 
					
						
							|  |  |  | 			err = phy_write(phydev, MII_CTRL1000, adv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (err < 0) | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			changed = 1; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  | 	return changed; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * genphy_setup_forced - configures/forces speed/duplex from @phydev | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Configures MII_BMCR to force speed/duplex | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   to the values in phydev. Assumes that the values are valid. | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  *   Please see phy_sanitize_settings(). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-11-20 16:38:19 -06:00
										 |  |  | int genphy_setup_forced(struct phy_device *phydev) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-08-17 08:54:45 +02:00
										 |  |  | 	int ctl = 0; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	phydev->pause = 0; | 
					
						
							|  |  |  | 	phydev->asym_pause = 0; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (SPEED_1000 == phydev->speed) | 
					
						
							|  |  |  | 		ctl |= BMCR_SPEED1000; | 
					
						
							|  |  |  | 	else if (SPEED_100 == phydev->speed) | 
					
						
							|  |  |  | 		ctl |= BMCR_SPEED100; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (DUPLEX_FULL == phydev->duplex) | 
					
						
							|  |  |  | 		ctl |= BMCR_FULLDPLX; | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:21:52 +03:00
										 |  |  | 	return phy_write(phydev, MII_BMCR, ctl); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-11-20 16:38:19 -06:00
										 |  |  | EXPORT_SYMBOL(genphy_setup_forced); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * genphy_restart_aneg - Enable and Restart Autonegotiation | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | int genphy_restart_aneg(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-01-05 03:23:19 +03:00
										 |  |  | 	int ctl = phy_read(phydev, MII_BMCR); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ctl < 0) | 
					
						
							|  |  |  | 		return ctl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	ctl |= BMCR_ANENABLE | BMCR_ANRESTART; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Don't isolate the PHY if we're negotiating */ | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	ctl &= ~BMCR_ISOLATE; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:23:19 +03:00
										 |  |  | 	return phy_write(phydev, MII_BMCR, ctl); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-10-13 18:48:09 -07:00
										 |  |  | EXPORT_SYMBOL(genphy_restart_aneg); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * genphy_config_aneg - restart auto-negotiation or write BMCR | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: If auto-negotiation is enabled, we configure the | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   advertising, and then restart auto-negotiation.  If it is not | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  *   enabled, then we write the BMCR. | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  */ | 
					
						
							|  |  |  | int genphy_config_aneg(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-19 15:52:41 -08:00
										 |  |  | 	int result; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-19 15:52:41 -08:00
										 |  |  | 	if (AUTONEG_ENABLE != phydev->autoneg) | 
					
						
							|  |  |  | 		return genphy_setup_forced(phydev); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-19 15:52:41 -08:00
										 |  |  | 	result = genphy_config_advert(phydev); | 
					
						
							|  |  |  | 	if (result < 0) /* error */ | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	if (result == 0) { | 
					
						
							| 
									
										
										
										
											2011-03-30 22:57:33 -03:00
										 |  |  | 		/* Advertisement hasn't changed, but maybe aneg was never on to
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 		 * begin with?  Or maybe phy was isolated? | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2008-11-19 15:52:41 -08:00
										 |  |  | 		int ctl = phy_read(phydev, MII_BMCR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ctl < 0) | 
					
						
							|  |  |  | 			return ctl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) | 
					
						
							|  |  |  | 			result = 1; /* do restart aneg */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Only restart aneg if we are advertising something different
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * than we were before. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2008-11-19 15:52:41 -08:00
										 |  |  | 	if (result > 0) | 
					
						
							|  |  |  | 		result = genphy_restart_aneg(phydev); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-24 10:55:46 +00:00
										 |  |  | 	return result; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(genphy_config_aneg); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * genphy_update_link - update link status in @phydev | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Update the value in phydev->link to reflect the | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   current link value.  In order to do this, we need to read | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  *   the status register twice, keeping the second value. | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  */ | 
					
						
							|  |  |  | int genphy_update_link(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Do a fake read */ | 
					
						
							|  |  |  | 	status = phy_read(phydev, MII_BMSR); | 
					
						
							|  |  |  | 	if (status < 0) | 
					
						
							|  |  |  | 		return status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Read link and autonegotiation status */ | 
					
						
							|  |  |  | 	status = phy_read(phydev, MII_BMSR); | 
					
						
							|  |  |  | 	if (status < 0) | 
					
						
							|  |  |  | 		return status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((status & BMSR_LSTATUS) == 0) | 
					
						
							|  |  |  | 		phydev->link = 0; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		phydev->link = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-10-16 16:19:17 -05:00
										 |  |  | EXPORT_SYMBOL(genphy_update_link); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * genphy_read_status - check the link status and update current link state | 
					
						
							|  |  |  |  * @phydev: target phy_device struct | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Check the link, then figure out the current state | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   by comparing what we advertise with what the link partner | 
					
						
							|  |  |  |  *   advertises.  Start by checking the gigabit possibilities, | 
					
						
							|  |  |  |  *   then move on to 10/100. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int genphy_read_status(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int adv; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	int lpa; | 
					
						
							|  |  |  | 	int lpagb = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	/* Update the link, but return if there was an error */ | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	err = genphy_update_link(phydev); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:30 -08:00
										 |  |  | 	phydev->lp_advertising = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	if (AUTONEG_ENABLE == phydev->autoneg) { | 
					
						
							|  |  |  | 		if (phydev->supported & (SUPPORTED_1000baseT_Half | 
					
						
							|  |  |  | 					| SUPPORTED_1000baseT_Full)) { | 
					
						
							|  |  |  | 			lpagb = phy_read(phydev, MII_STAT1000); | 
					
						
							|  |  |  | 			if (lpagb < 0) | 
					
						
							|  |  |  | 				return lpagb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			adv = phy_read(phydev, MII_CTRL1000); | 
					
						
							|  |  |  | 			if (adv < 0) | 
					
						
							|  |  |  | 				return adv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:30 -08:00
										 |  |  | 			phydev->lp_advertising = | 
					
						
							|  |  |  | 				mii_stat1000_to_ethtool_lpa_t(lpagb); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 			lpagb &= adv << 2; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		lpa = phy_read(phydev, MII_LPA); | 
					
						
							|  |  |  | 		if (lpa < 0) | 
					
						
							|  |  |  | 			return lpa; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-06 13:01:30 -08:00
										 |  |  | 		phydev->lp_advertising |= mii_lpa_to_ethtool_lpa_t(lpa); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 		adv = phy_read(phydev, MII_ADVERTISE); | 
					
						
							|  |  |  | 		if (adv < 0) | 
					
						
							|  |  |  | 			return adv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		lpa &= adv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		phydev->speed = SPEED_10; | 
					
						
							|  |  |  | 		phydev->duplex = DUPLEX_HALF; | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 		phydev->pause = 0; | 
					
						
							|  |  |  | 		phydev->asym_pause = 0; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { | 
					
						
							|  |  |  | 			phydev->speed = SPEED_1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (lpagb & LPA_1000FULL) | 
					
						
							|  |  |  | 				phydev->duplex = DUPLEX_FULL; | 
					
						
							|  |  |  | 		} else if (lpa & (LPA_100FULL | LPA_100HALF)) { | 
					
						
							|  |  |  | 			phydev->speed = SPEED_100; | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 			if (lpa & LPA_100FULL) | 
					
						
							|  |  |  | 				phydev->duplex = DUPLEX_FULL; | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			if (lpa & LPA_10FULL) | 
					
						
							|  |  |  | 				phydev->duplex = DUPLEX_FULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | 		if (phydev->duplex == DUPLEX_FULL) { | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 			phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; | 
					
						
							|  |  |  | 			phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		int bmcr = phy_read(phydev, MII_BMCR); | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 		if (bmcr < 0) | 
					
						
							|  |  |  | 			return bmcr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (bmcr & BMCR_FULLDPLX) | 
					
						
							|  |  |  | 			phydev->duplex = DUPLEX_FULL; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			phydev->duplex = DUPLEX_HALF; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (bmcr & BMCR_SPEED1000) | 
					
						
							|  |  |  | 			phydev->speed = SPEED_1000; | 
					
						
							|  |  |  | 		else if (bmcr & BMCR_SPEED100) | 
					
						
							|  |  |  | 			phydev->speed = SPEED_100; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			phydev->speed = SPEED_10; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 		phydev->pause = 0; | 
					
						
							|  |  |  | 		phydev->asym_pause = 0; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(genphy_read_status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int genphy_config_init(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-09-25 16:39:22 -07:00
										 |  |  | 	int val; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	u32 features; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* For now, I'll claim that the generic driver supports
 | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * all possible port types | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	features = (SUPPORTED_TP | SUPPORTED_MII | 
					
						
							|  |  |  | 			| SUPPORTED_AUI | SUPPORTED_FIBRE | | 
					
						
							|  |  |  | 			SUPPORTED_BNC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Do we support autonegotiation? */ | 
					
						
							|  |  |  | 	val = phy_read(phydev, MII_BMSR); | 
					
						
							|  |  |  | 	if (val < 0) | 
					
						
							|  |  |  | 		return val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (val & BMSR_ANEGCAPABLE) | 
					
						
							|  |  |  | 		features |= SUPPORTED_Autoneg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (val & BMSR_100FULL) | 
					
						
							|  |  |  | 		features |= SUPPORTED_100baseT_Full; | 
					
						
							|  |  |  | 	if (val & BMSR_100HALF) | 
					
						
							|  |  |  | 		features |= SUPPORTED_100baseT_Half; | 
					
						
							|  |  |  | 	if (val & BMSR_10FULL) | 
					
						
							|  |  |  | 		features |= SUPPORTED_10baseT_Full; | 
					
						
							|  |  |  | 	if (val & BMSR_10HALF) | 
					
						
							|  |  |  | 		features |= SUPPORTED_10baseT_Half; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (val & BMSR_ESTATEN) { | 
					
						
							|  |  |  | 		val = phy_read(phydev, MII_ESTATUS); | 
					
						
							|  |  |  | 		if (val < 0) | 
					
						
							|  |  |  | 			return val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (val & ESTATUS_1000_TFULL) | 
					
						
							|  |  |  | 			features |= SUPPORTED_1000baseT_Full; | 
					
						
							|  |  |  | 		if (val & ESTATUS_1000_THALF) | 
					
						
							|  |  |  | 			features |= SUPPORTED_1000baseT_Half; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	phydev->supported = features; | 
					
						
							|  |  |  | 	phydev->advertising = features; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2008-11-28 16:24:56 -08:00
										 |  |  | int genphy_suspend(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&phydev->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value = phy_read(phydev, MII_BMCR); | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); | 
					
						
							| 
									
										
										
										
											2008-11-28 16:24:56 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&phydev->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(genphy_suspend); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-28 16:24:56 -08:00
										 |  |  | int genphy_resume(struct phy_device *phydev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&phydev->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value = phy_read(phydev, MII_BMCR); | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); | 
					
						
							| 
									
										
										
										
											2008-11-28 16:24:56 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&phydev->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(genphy_resume); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_probe - probe and init a PHY device | 
					
						
							|  |  |  |  * @dev: device to probe and init | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  |  * Description: Take care of setting up the phy_device structure, | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  |  *   set the state to READY (the driver's init function should | 
					
						
							|  |  |  |  *   set it to STARTING if needed). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int phy_probe(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-01-05 03:23:19 +03:00
										 |  |  | 	struct phy_device *phydev = to_phy_device(dev); | 
					
						
							|  |  |  | 	struct device_driver *drv = phydev->dev.driver; | 
					
						
							|  |  |  | 	struct phy_driver *phydrv = to_phy_driver(drv); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	phydev->drv = phydrv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-19 22:53:42 +00:00
										 |  |  | 	/* Disable the interrupt if the PHY doesn't support it
 | 
					
						
							|  |  |  | 	 * but the interrupt is still a valid one | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!(phydrv->flags & PHY_HAS_INTERRUPT) && | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	    phy_interrupt_is_valid(phydev)) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 		phydev->irq = PHY_POLL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-23 01:11:12 +00:00
										 |  |  | 	if (phydrv->flags & PHY_IS_INTERNAL) | 
					
						
							|  |  |  | 		phydev->is_internal = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-29 10:05:09 -06:00
										 |  |  | 	mutex_lock(&phydev->lock); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Start out supporting everything. Eventually,
 | 
					
						
							|  |  |  | 	 * a controller will attach, and may modify one | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 	 * or both of these values | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	phydev->supported = phydrv->features; | 
					
						
							|  |  |  | 	phydev->advertising = phydrv->features; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set the state to READY by default */ | 
					
						
							|  |  |  | 	phydev->state = PHY_READY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (phydev->drv->probe) | 
					
						
							|  |  |  | 		err = phydev->drv->probe(phydev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-29 10:05:09 -06:00
										 |  |  | 	mutex_unlock(&phydev->lock); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int phy_remove(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-01-05 03:23:19 +03:00
										 |  |  | 	struct phy_device *phydev = to_phy_device(dev); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-29 10:05:09 -06:00
										 |  |  | 	mutex_lock(&phydev->lock); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 	phydev->state = PHY_DOWN; | 
					
						
							| 
									
										
										
										
											2008-01-29 10:05:09 -06:00
										 |  |  | 	mutex_unlock(&phydev->lock); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (phydev->drv->remove) | 
					
						
							|  |  |  | 		phydev->drv->remove(phydev); | 
					
						
							|  |  |  | 	phydev->drv = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-06 02:41:48 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * phy_driver_register - register a phy_driver with the PHY layer | 
					
						
							|  |  |  |  * @new_driver: new phy_driver to register | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | int phy_driver_register(struct phy_driver *new_driver) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	new_driver->driver.name = new_driver->name; | 
					
						
							|  |  |  | 	new_driver->driver.bus = &mdio_bus_type; | 
					
						
							|  |  |  | 	new_driver->driver.probe = phy_probe; | 
					
						
							|  |  |  | 	new_driver->driver.remove = phy_remove; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	retval = driver_register(&new_driver->driver); | 
					
						
							|  |  |  | 	if (retval) { | 
					
						
							| 
									
										
										
										
											2012-06-09 07:49:07 +00:00
										 |  |  | 		pr_err("%s: Error %d in registering driver\n", | 
					
						
							|  |  |  | 		       new_driver->name, retval); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return retval; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-04 16:09:23 -06:00
										 |  |  | 	pr_debug("%s: Registered new driver\n", new_driver->name); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_driver_register); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-04 05:44:34 +00:00
										 |  |  | int phy_drivers_register(struct phy_driver *new_driver, int n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < n; i++) { | 
					
						
							|  |  |  | 		ret = phy_driver_register(new_driver + i); | 
					
						
							|  |  |  | 		if (ret) { | 
					
						
							|  |  |  | 			while (i-- > 0) | 
					
						
							|  |  |  | 				phy_driver_unregister(new_driver + i); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_drivers_register); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | void phy_driver_unregister(struct phy_driver *drv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	driver_unregister(&drv->driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_driver_unregister); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-04 05:44:34 +00:00
										 |  |  | void phy_drivers_unregister(struct phy_driver *drv, int n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2014-01-05 03:17:06 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < n; i++) | 
					
						
							| 
									
										
										
										
											2012-07-04 05:44:34 +00:00
										 |  |  | 		phy_driver_unregister(drv + i); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(phy_drivers_unregister); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | static struct phy_driver genphy_driver = { | 
					
						
							|  |  |  | 	.phy_id		= 0xffffffff, | 
					
						
							|  |  |  | 	.phy_id_mask	= 0xffffffff, | 
					
						
							|  |  |  | 	.name		= "Generic PHY", | 
					
						
							|  |  |  | 	.config_init	= genphy_config_init, | 
					
						
							|  |  |  | 	.features	= 0, | 
					
						
							|  |  |  | 	.config_aneg	= genphy_config_aneg, | 
					
						
							|  |  |  | 	.read_status	= genphy_read_status, | 
					
						
							| 
									
										
										
										
											2008-11-28 16:24:56 -08:00
										 |  |  | 	.suspend	= genphy_suspend, | 
					
						
							|  |  |  | 	.resume		= genphy_resume, | 
					
						
							| 
									
										
										
										
											2013-12-17 21:38:12 -08:00
										 |  |  | 	.driver		= { .owner = THIS_MODULE, }, | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-11 02:07:25 -04:00
										 |  |  | static int __init phy_init(void) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-08-11 02:07:25 -04:00
										 |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = mdio_bus_init(); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 		return rc; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	rc = phy_driver_register(&genphy_driver); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		mdio_bus_exit(); | 
					
						
							| 
									
										
										
										
											2005-08-11 02:07:25 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return rc; | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-11 02:07:25 -04:00
										 |  |  | static void __exit phy_exit(void) | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	phy_driver_unregister(&genphy_driver); | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | 	mdio_bus_exit(); | 
					
						
							| 
									
										
										
										
											2005-07-30 19:31:23 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-24 18:46:21 -05:00
										 |  |  | subsys_initcall(phy_init); | 
					
						
							| 
									
										
										
										
											2005-08-11 02:07:25 -04:00
										 |  |  | module_exit(phy_exit); |