/* Loop until the read is completed with timeout
* called with phy_mutex held */ static __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev, int in_pm)
{ unsignedlong start_time = jiffies;
u32 val; int ret;
do {
ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Error reading MII_ACCESS\n"); return ret;
}
if (!(val & MII_ACCESS_BUSY)) return 0;
} while (!time_after(jiffies, start_time + HZ));
return -EIO;
}
staticint __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx, int in_pm)
{ struct usbnet *dev = netdev_priv(netdev);
u32 val, addr; int ret;
mutex_lock(&dev->phy_mutex);
/* confirm MII not busy */
ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); if (ret < 0) {
netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_read\n"); goto done;
}
/* set the address, index & direction (read from PHY) */
phy_id &= dev->mii.phy_id_mask;
idx &= dev->mii.reg_num_mask;
addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR)
| ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR)
| MII_ACCESS_READ | MII_ACCESS_BUSY;
ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Error writing MII_ACCESS\n"); goto done;
}
ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); goto done;
}
ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Error reading MII_DATA\n"); goto done;
}
staticvoid __smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, int regval, int in_pm)
{ struct usbnet *dev = netdev_priv(netdev);
u32 val, addr; int ret;
mutex_lock(&dev->phy_mutex);
/* confirm MII not busy */
ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); if (ret < 0) {
netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_write\n"); goto done;
}
val = regval;
ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Error writing MII_DATA\n"); goto done;
}
/* set the address, index & direction (write to PHY) */
phy_id &= dev->mii.phy_id_mask;
idx &= dev->mii.reg_num_mask;
addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR)
| ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR)
| MII_ACCESS_WRITE | MII_ACCESS_BUSY;
ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Error writing MII_ACCESS\n"); goto done;
}
ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); if (ret < 0) {
netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); goto done;
}
done:
mutex_unlock(&dev->phy_mutex);
}
staticint smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id, int idx)
{ return __smsc75xx_mdio_read(netdev, phy_id, idx, 1);
}
staticvoid smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id, int idx, int regval)
{
__smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1);
}
staticint smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx)
{ return __smsc75xx_mdio_read(netdev, phy_id, idx, 0);
}
staticvoid smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, int regval)
{
__smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0);
}
do {
ret = smsc75xx_read_reg(dev, E2P_CMD, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading E2P_CMD\n"); return ret;
}
if (!(val & E2P_CMD_BUSY)) return 0;
udelay(40);
} while (!time_after(jiffies, start_time + HZ));
netdev_warn(dev->net, "EEPROM is busy\n"); return -EIO;
}
staticint smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length,
u8 *data)
{
u32 val; int i, ret;
BUG_ON(!dev);
BUG_ON(!data);
ret = smsc75xx_eeprom_confirm_not_busy(dev); if (ret) return ret;
for (i = 0; i < length; i++) {
val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR);
ret = smsc75xx_write_reg(dev, E2P_CMD, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing E2P_CMD\n"); return ret;
}
ret = smsc75xx_wait_eeprom(dev); if (ret < 0) return ret;
ret = smsc75xx_read_reg(dev, E2P_DATA, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading E2P_DATA\n"); return ret;
}
data[i] = val & 0xFF;
offset++;
}
return 0;
}
staticint smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,
u8 *data)
{
u32 val; int i, ret;
BUG_ON(!dev);
BUG_ON(!data);
ret = smsc75xx_eeprom_confirm_not_busy(dev); if (ret) return ret;
/* Issue write/erase enable command */
val = E2P_CMD_BUSY | E2P_CMD_EWEN;
ret = smsc75xx_write_reg(dev, E2P_CMD, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing E2P_CMD\n"); return ret;
}
ret = smsc75xx_wait_eeprom(dev); if (ret < 0) return ret;
for (i = 0; i < length; i++) {
/* Fill data register */
val = data[i];
ret = smsc75xx_write_reg(dev, E2P_DATA, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing E2P_DATA\n"); return ret;
}
/* returns hash bit number for given MAC address */ static u32 smsc75xx_hash(char addr[ETH_ALEN])
{ return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff;
}
/* maybe the boot loader passed the MAC address in devicetree */ if (!platform_get_ethdev_address(&dev->udev->dev, dev->net)) { if (is_valid_ether_addr(dev->net->dev_addr)) { /* device tree values are valid so use them */
netif_dbg(dev, ifup, dev->net, "MAC address read from the device tree\n"); return;
}
}
/* try reading mac address from EEPROM */ if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, addr) == 0) {
eth_hw_addr_set(dev->net, addr); if (is_valid_ether_addr(dev->net->dev_addr)) { /* eeprom values are valid so use them */
netif_dbg(dev, ifup, dev->net, "MAC address read from EEPROM\n"); return;
}
}
/* no useful static MAC address found. generate a random one */
eth_hw_addr_random(dev->net);
netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n");
}
staticint smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size)
{ int ret = 0;
u32 buf; bool rxenabled;
ret = smsc75xx_read_reg(dev, MAC_RX, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); return ret;
}
rxenabled = ((buf & MAC_RX_RXEN) != 0);
if (rxenabled) {
buf &= ~MAC_RX_RXEN;
ret = smsc75xx_write_reg(dev, MAC_RX, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); return ret;
}
}
/* add 4 to size for FCS */
buf &= ~MAC_RX_MAX_SIZE;
buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE);
ret = smsc75xx_write_reg(dev, MAC_RX, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); return ret;
}
if (rxenabled) {
buf |= MAC_RX_RXEN;
ret = smsc75xx_write_reg(dev, MAC_RX, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); return ret;
}
}
return 0;
}
staticint smsc75xx_change_mtu(struct net_device *netdev, int new_mtu)
{ struct usbnet *dev = netdev_priv(netdev); int ret;
ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu + ETH_HLEN); if (ret < 0) {
netdev_warn(dev->net, "Failed to set mac rx frame length\n"); return ret;
}
ret = smsc75xx_read_reg(dev, E2P_CMD, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read E2P_CMD: %d\n", ret); return ret;
}
/* only set default GPIO/LED settings if no EEPROM is detected */ if (!(buf & E2P_CMD_LOADED)) {
ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read LED_GPIO_CFG: %d\n", ret); return ret;
}
ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); return ret;
}
/* allow mac to detect speed and duplex from phy */
ret = smsc75xx_read_reg(dev, MAC_CR, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); return ret;
}
buf |= (MAC_CR_ADD | MAC_CR_ASD);
ret = smsc75xx_write_reg(dev, MAC_CR, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); return ret;
}
ret = smsc75xx_read_reg(dev, MAC_TX, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read MAC_TX: %d\n", ret); return ret;
}
buf |= MAC_TX_TXEN;
ret = smsc75xx_write_reg(dev, MAC_TX, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_TX: %d\n", ret); return ret;
}
netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf);
ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read FCT_TX_CTL: %d\n", ret); return ret;
}
buf |= FCT_TX_CTL_EN;
ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write FCT_TX_CTL: %d\n", ret); return ret;
}
netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf);
ret = smsc75xx_set_rx_max_frame_length(dev, dev->net->mtu + ETH_HLEN); if (ret < 0) {
netdev_warn(dev->net, "Failed to set max rx frame length\n"); return ret;
}
ret = smsc75xx_read_reg(dev, MAC_RX, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); return ret;
}
buf |= MAC_RX_RXEN;
ret = smsc75xx_write_reg(dev, MAC_RX, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); return ret;
}
netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf);
ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to read FCT_RX_CTL: %d\n", ret); return ret;
}
buf |= FCT_RX_CTL_EN;
ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf); if (ret < 0) {
netdev_warn(dev->net, "Failed to write FCT_RX_CTL: %d\n", ret); return ret;
}
netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x\n", buf);
/* first, a dummy read, needed to latch some MII phys */
ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); if (ret < 0) {
netdev_warn(dev->net, "Error reading MII_BMSR\n"); return ret;
}
ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); if (ret < 0) {
netdev_warn(dev->net, "Error reading MII_BMSR\n"); return ret;
}
return !!(ret & BMSR_LSTATUS);
}
staticint smsc75xx_autosuspend(struct usbnet *dev, u32 link_up)
{ int ret;
if (!netif_running(dev->net)) { /* interface is ifconfig down so fully power down hw */
netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); return smsc75xx_enter_suspend2(dev);
}
if (!link_up) { /* link is down so enter EDPD mode */
netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
/* enable PHY wakeup events for if cable is attached */
ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
PHY_INT_MASK_ANEG_COMP); if (ret < 0) {
netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); return ret;
}
ret = usbnet_suspend(intf, message); if (ret < 0) {
netdev_warn(dev->net, "usbnet_suspend error\n"); return ret;
}
if (pdata->suspend_flags) {
netdev_warn(dev->net, "error during last resume\n");
pdata->suspend_flags = 0;
}
/* determine if link is up using only _nopm functions */
link_up = smsc75xx_link_ok_nopm(dev);
if (message.event == PM_EVENT_AUTO_SUSPEND) {
ret = smsc75xx_autosuspend(dev, link_up); goto done;
}
/* if we get this far we're not autosuspending */ /* if no wol options set, or if link is down and we're not waking on * PHY activity, enter lowest power SUSPEND2 mode
*/ if (!(pdata->wolopts & SUPPORTED_WAKE) ||
!(link_up || (pdata->wolopts & WAKE_PHY))) {
netdev_info(dev->net, "entering SUSPEND2 mode\n");
/* disable energy detect (link up) & wake up events */
ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading WUCSR\n"); goto done;
}
val &= ~(WUCSR_MPEN | WUCSR_WUEN);
ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing WUCSR\n"); goto done;
}
ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading PMT_CTL\n"); goto done;
}
val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN);
ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing PMT_CTL\n"); goto done;
}
ret = smsc75xx_enter_suspend2(dev); goto done;
}
if (pdata->wolopts & WAKE_PHY) {
ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
(PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN)); if (ret < 0) {
netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); goto done;
}
/* if link is down then configure EDPD and enter SUSPEND1, * otherwise enter SUSPEND0 below
*/ if (!link_up) { struct mii_if_info *mii = &dev->mii;
netdev_info(dev->net, "entering SUSPEND1 mode\n");
/* enable energy detect power-down mode */
ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id,
PHY_MODE_CTRL_STS); if (ret < 0) {
netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n"); goto done;
}
val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN);
ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing WUCSR\n"); goto done;
}
if (pdata->wolopts & WAKE_PHY) {
netdev_info(dev->net, "enabling PHY wakeup\n");
ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading PMT_CTL\n"); goto done;
}
/* clear wol status, enable energy detection */
val &= ~PMT_CTL_WUPS;
val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN);
ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing PMT_CTL\n"); goto done;
}
}
if (pdata->wolopts & WAKE_MAGIC) {
netdev_info(dev->net, "enabling magic packet wakeup\n");
ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading WUCSR\n"); goto done;
}
/* clear any pending magic packet status */
val |= WUCSR_MPR | WUCSR_MPEN;
ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing WUCSR\n"); goto done;
}
}
if (pdata->wolopts & WAKE_BCAST) {
netdev_info(dev->net, "enabling broadcast detection\n");
ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading WUCSR\n"); goto done;
}
val |= WUCSR_BCAST_FR | WUCSR_BCST_EN;
ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing WUCSR\n"); goto done;
}
}
if (pdata->wolopts & WAKE_UCAST) {
netdev_info(dev->net, "enabling unicast detection\n");
ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); if (ret < 0) {
netdev_warn(dev->net, "Error reading WUCSR\n"); goto done;
}
val |= WUCSR_WUFR | WUCSR_PFDA_EN;
ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); if (ret < 0) {
netdev_warn(dev->net, "Error writing WUCSR\n"); goto done;
}
}
/* enable receiver to enable frame reception */
ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val); if (ret < 0) {
netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); goto done;
}
val |= MAC_RX_RXEN;
ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val); if (ret < 0) {
netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); goto done;
}
/* some wol options are enabled, so enter SUSPEND0 */
netdev_info(dev->net, "entering SUSPEND0 mode\n");
ret = smsc75xx_enter_suspend0(dev);
done: /* * TODO: resume() might need to handle the suspend failure * in system sleep
*/ if (ret && PMSG_IS_AUTO(message))
usbnet_resume(intf); return ret;
}
staticint smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{ /* This check is no longer done by usbnet */ if (skb->len < dev->net->hard_header_len) return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.