diff options
Diffstat (limited to 'drivers/net/usb/lan78xx.c')
| -rw-r--r-- | drivers/net/usb/lan78xx.c | 235 | 
1 files changed, 180 insertions, 55 deletions
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 0867f7275852..8dff87ec6d99 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -36,13 +36,14 @@  #include <linux/irq.h>  #include <linux/irqchip/chained_irq.h>  #include <linux/microchipphy.h> -#include <linux/phy.h> +#include <linux/phy_fixed.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h>  #include "lan78xx.h"  #define DRIVER_AUTHOR	"WOOJUNG HUH <woojung.huh@microchip.com>"  #define DRIVER_DESC	"LAN78XX USB 3.0 Gigabit Ethernet Devices"  #define DRIVER_NAME	"lan78xx" -#define DRIVER_VERSION	"1.0.6"  #define TX_TIMEOUT_JIFFIES		(5 * HZ)  #define THROTTLE_JIFFIES		(HZ / 8) @@ -278,6 +279,30 @@ struct lan78xx_statstage64 {  	u64 eee_tx_lpi_time;  }; +static u32 lan78xx_regs[] = { +	ID_REV, +	INT_STS, +	HW_CFG, +	PMT_CTL, +	E2P_CMD, +	E2P_DATA, +	USB_STATUS, +	VLAN_TYPE, +	MAC_CR, +	MAC_RX, +	MAC_TX, +	FLOW, +	ERR_STS, +	MII_ACC, +	MII_DATA, +	EEE_TX_LPI_REQ_DLY, +	EEE_TW_TX_SYS, +	EEE_TX_LPI_REM_DLY, +	WUCSR +}; + +#define PHY_REG_SIZE (32 * sizeof(u32)) +  struct lan78xx_net;  struct lan78xx_priv { @@ -1477,7 +1502,6 @@ static void lan78xx_get_drvinfo(struct net_device *net,  	struct lan78xx_net *dev = netdev_priv(net);  	strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); -	strncpy(info->version, DRIVER_VERSION, sizeof(info->version));  	usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));  } @@ -1605,6 +1629,34 @@ exit:  	return ret;  } +static int lan78xx_get_regs_len(struct net_device *netdev) +{ +	if (!netdev->phydev) +		return (sizeof(lan78xx_regs)); +	else +		return (sizeof(lan78xx_regs) + PHY_REG_SIZE); +} + +static void +lan78xx_get_regs(struct net_device *netdev, struct ethtool_regs *regs, +		 void *buf) +{ +	u32 *data = buf; +	int i, j; +	struct lan78xx_net *dev = netdev_priv(netdev); + +	/* Read Device/MAC registers */ +	for (i = 0; i < (sizeof(lan78xx_regs) / sizeof(u32)); i++) +		lan78xx_read_reg(dev, lan78xx_regs[i], &data[i]); + +	if (!netdev->phydev) +		return; + +	/* Read PHY registers */ +	for (j = 0; j < 32; i++, j++) +		data[i] = phy_read(netdev->phydev, j); +} +  static const struct ethtool_ops lan78xx_ethtool_ops = {  	.get_link	= lan78xx_get_link,  	.nway_reset	= phy_ethtool_nway_reset, @@ -1625,6 +1677,8 @@ static const struct ethtool_ops lan78xx_ethtool_ops = {  	.set_pauseparam	= lan78xx_set_pause,  	.get_link_ksettings = lan78xx_get_link_ksettings,  	.set_link_ksettings = lan78xx_set_link_ksettings, +	.get_regs_len	= lan78xx_get_regs_len, +	.get_regs	= lan78xx_get_regs,  };  static int lan78xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) @@ -1652,34 +1706,31 @@ static void lan78xx_init_mac_address(struct lan78xx_net *dev)  	addr[5] = (addr_hi >> 8) & 0xFF;  	if (!is_valid_ether_addr(addr)) { -		/* reading mac address from EEPROM or OTP */ -		if ((lan78xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, -					 addr) == 0) || -		    (lan78xx_read_otp(dev, EEPROM_MAC_OFFSET, ETH_ALEN, -				      addr) == 0)) { -			if (is_valid_ether_addr(addr)) { -				/* eeprom values are valid so use them */ -				netif_dbg(dev, ifup, dev->net, -					  "MAC address read from EEPROM"); -			} else { -				/* generate random MAC */ -				random_ether_addr(addr); -				netif_dbg(dev, ifup, dev->net, -					  "MAC address set to random addr"); -			} - -			addr_lo = addr[0] | (addr[1] << 8) | -				  (addr[2] << 16) | (addr[3] << 24); -			addr_hi = addr[4] | (addr[5] << 8); - -			ret = lan78xx_write_reg(dev, RX_ADDRL, addr_lo); -			ret = lan78xx_write_reg(dev, RX_ADDRH, addr_hi); +		if (!eth_platform_get_mac_address(&dev->udev->dev, addr)) { +			/* valid address present in Device Tree */ +			netif_dbg(dev, ifup, dev->net, +				  "MAC address read from Device Tree"); +		} else if (((lan78xx_read_eeprom(dev, EEPROM_MAC_OFFSET, +						 ETH_ALEN, addr) == 0) || +			    (lan78xx_read_otp(dev, EEPROM_MAC_OFFSET, +					      ETH_ALEN, addr) == 0)) && +			   is_valid_ether_addr(addr)) { +			/* eeprom values are valid so use them */ +			netif_dbg(dev, ifup, dev->net, +				  "MAC address read from EEPROM");  		} else {  			/* generate random MAC */  			random_ether_addr(addr);  			netif_dbg(dev, ifup, dev->net,  				  "MAC address set to random addr");  		} + +		addr_lo = addr[0] | (addr[1] << 8) | +			  (addr[2] << 16) | (addr[3] << 24); +		addr_hi = addr[4] | (addr[5] << 8); + +		ret = lan78xx_write_reg(dev, RX_ADDRL, addr_lo); +		ret = lan78xx_write_reg(dev, RX_ADDRH, addr_hi);  	}  	ret = lan78xx_write_reg(dev, MAF_LO(0), addr_lo); @@ -1762,6 +1813,7 @@ done:  static int lan78xx_mdio_init(struct lan78xx_net *dev)  { +	struct device_node *node;  	int ret;  	dev->mdiobus = mdiobus_alloc(); @@ -1790,7 +1842,10 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)  		break;  	} -	ret = mdiobus_register(dev->mdiobus); +	node = of_get_child_by_name(dev->udev->dev.of_node, "mdio"); +	ret = of_mdiobus_register(dev->mdiobus, node); +	if (node) +		of_node_put(node);  	if (ret) {  		netdev_err(dev->net, "can't register MDIO bus\n");  		goto exit1; @@ -2003,52 +2058,91 @@ static int ksz9031rnx_fixup(struct phy_device *phydev)  	return 1;  } -static int lan78xx_phy_init(struct lan78xx_net *dev) +static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)  { +	u32 buf;  	int ret; -	u32 mii_adv; +	struct fixed_phy_status fphy_status = { +		.link = 1, +		.speed = SPEED_1000, +		.duplex = DUPLEX_FULL, +	};  	struct phy_device *phydev;  	phydev = phy_find_first(dev->mdiobus);  	if (!phydev) { -		netdev_err(dev->net, "no PHY found\n"); -		return -EIO; -	} - -	if ((dev->chipid == ID_REV_CHIP_ID_7800_) || -	    (dev->chipid == ID_REV_CHIP_ID_7850_)) { -		phydev->is_internal = true; -		dev->interface = PHY_INTERFACE_MODE_GMII; - -	} else if (dev->chipid == ID_REV_CHIP_ID_7801_) { +		netdev_dbg(dev->net, "PHY Not Found!! Registering Fixed PHY\n"); +		phydev = fixed_phy_register(PHY_POLL, &fphy_status, -1, +					    NULL); +		if (IS_ERR(phydev)) { +			netdev_err(dev->net, "No PHY/fixed_PHY found\n"); +			return NULL; +		} +		netdev_dbg(dev->net, "Registered FIXED PHY\n"); +		dev->interface = PHY_INTERFACE_MODE_RGMII; +		ret = lan78xx_write_reg(dev, MAC_RGMII_ID, +					MAC_RGMII_ID_TXC_DELAY_EN_); +		ret = lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00); +		ret = lan78xx_read_reg(dev, HW_CFG, &buf); +		buf |= HW_CFG_CLK125_EN_; +		buf |= HW_CFG_REFCLK25_EN_; +		ret = lan78xx_write_reg(dev, HW_CFG, buf); +	} else {  		if (!phydev->drv) {  			netdev_err(dev->net, "no PHY driver found\n"); -			return -EIO; +			return NULL;  		} -  		dev->interface = PHY_INTERFACE_MODE_RGMII; -  		/* external PHY fixup for KSZ9031RNX */  		ret = phy_register_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0,  						 ksz9031rnx_fixup);  		if (ret < 0) { -			netdev_err(dev->net, "fail to register fixup\n"); -			return ret; +			netdev_err(dev->net, "Failed to register fixup for PHY_KSZ9031RNX\n"); +			return NULL;  		}  		/* external PHY fixup for LAN8835 */  		ret = phy_register_fixup_for_uid(PHY_LAN8835, 0xfffffff0,  						 lan8835_fixup);  		if (ret < 0) { -			netdev_err(dev->net, "fail to register fixup\n"); -			return ret; +			netdev_err(dev->net, "Failed to register fixup for PHY_LAN8835\n"); +			return NULL;  		}  		/* add more external PHY fixup here if needed */  		phydev->is_internal = false; -	} else { -		netdev_err(dev->net, "unknown ID found\n"); -		ret = -EIO; -		goto error; +	} +	return phydev; +} + +static int lan78xx_phy_init(struct lan78xx_net *dev) +{ +	int ret; +	u32 mii_adv; +	struct phy_device *phydev; + +	switch (dev->chipid) { +	case ID_REV_CHIP_ID_7801_: +		phydev = lan7801_phy_init(dev); +		if (!phydev) { +			netdev_err(dev->net, "lan7801: PHY Init Failed"); +			return -EIO; +		} +		break; + +	case ID_REV_CHIP_ID_7800_: +	case ID_REV_CHIP_ID_7850_: +		phydev = phy_find_first(dev->mdiobus); +		if (!phydev) { +			netdev_err(dev->net, "no PHY found\n"); +			return -EIO; +		} +		phydev->is_internal = true; +		dev->interface = PHY_INTERFACE_MODE_GMII; +		break; + +	default: +		netdev_err(dev->net, "Unknown CHIP ID found\n"); +		return -EIO;  	}  	/* if phyirq is not set, use polling mode in phylib */ @@ -2067,6 +2161,16 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)  	if (ret) {  		netdev_err(dev->net, "can't attach PHY to %s\n",  			   dev->mdiobus->id); +		if (dev->chipid == ID_REV_CHIP_ID_7801_) { +			if (phy_is_pseudo_fixed_link(phydev)) { +				fixed_phy_unregister(phydev); +			} else { +				phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, +							     0xfffffff0); +				phy_unregister_fixup_for_uid(PHY_LAN8835, +							     0xfffffff0); +			} +		}  		return -EIO;  	} @@ -2079,17 +2183,33 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)  	mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);  	phydev->advertising |= mii_adv_to_ethtool_adv_t(mii_adv); +	if (phydev->mdio.dev.of_node) { +		u32 reg; +		int len; + +		len = of_property_count_elems_of_size(phydev->mdio.dev.of_node, +						      "microchip,led-modes", +						      sizeof(u32)); +		if (len >= 0) { +			/* Ensure the appropriate LEDs are enabled */ +			lan78xx_read_reg(dev, HW_CFG, ®); +			reg &= ~(HW_CFG_LED0_EN_ | +				 HW_CFG_LED1_EN_ | +				 HW_CFG_LED2_EN_ | +				 HW_CFG_LED3_EN_); +			reg |= (len > 0) * HW_CFG_LED0_EN_ | +				(len > 1) * HW_CFG_LED1_EN_ | +				(len > 2) * HW_CFG_LED2_EN_ | +				(len > 3) * HW_CFG_LED3_EN_; +			lan78xx_write_reg(dev, HW_CFG, reg); +		} +	} +  	genphy_config_aneg(phydev);  	dev->fc_autoneg = phydev->autoneg;  	return 0; - -error: -	phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0); -	phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0); - -	return ret;  }  static int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size) @@ -3487,6 +3607,7 @@ static void lan78xx_disconnect(struct usb_interface *intf)  	struct lan78xx_net		*dev;  	struct usb_device		*udev;  	struct net_device		*net; +	struct phy_device		*phydev;  	dev = usb_get_intfdata(intf);  	usb_set_intfdata(intf, NULL); @@ -3495,12 +3616,16 @@ static void lan78xx_disconnect(struct usb_interface *intf)  	udev = interface_to_usbdev(intf);  	net = dev->net; +	phydev = net->phydev;  	phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0);  	phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0);  	phy_disconnect(net->phydev); +	if (phy_is_pseudo_fixed_link(phydev)) +		fixed_phy_unregister(phydev); +  	unregister_netdev(net);  	cancel_delayed_work_sync(&dev->wq);  | 
