// SPDX-License-Identifier: GPL-2.0-or-later /* * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices * * based on usbnet.c, asix.c and the vendor provided mcs7830 driver * * Copyright (C) 2010 Andreas Mohr <andi@lisas.de> * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de> * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com> * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> * Copyright (c) 2002-2003 TiVo Inc. * * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!). * * 2010-12-19: add 7832 USB PID ("functionality same as MCS7830"), * per active notification by manufacturer * * TODO: * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?) * - implement ethtool_ops get_pauseparam/set_pauseparam * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!) * - implement get_eeprom/[set_eeprom] * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII) * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs, * can access only ~ 24, remaining user buffer is uninitialized garbage * - anything else?
*/
mutex_lock(&dev->phy_mutex); /* write the MII command */
ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); if (ret < 0) goto out;
/* wait for the data to become valid, should be within < 1ms */ for (i = 0; i < 10; i++) {
ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT)) break;
ret = -EIO;
msleep(1);
} if (ret < 0) goto out;
/* read actual register contents */
ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val); if (ret < 0) goto out;
ret = le16_to_cpu(val);
dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)\n",
index, val, i);
out:
mutex_unlock(&dev->phy_mutex); return ret;
}
staticint mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val)
{ int ret; int i;
__le16 le_val;
/* write the new register contents */
le_val = cpu_to_le16(val);
ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val); if (ret < 0) goto out;
/* write the MII command */
ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); if (ret < 0) goto out;
/* wait for the command to be accepted by the PHY */ for (i = 0; i < 10; i++) {
ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT)) break;
ret = -EIO;
msleep(1);
} if (ret < 0) goto out;
/* * This algorithm comes from the original mcs7830 version 1.4 driver, * not sure if it is needed.
*/ staticint mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode)
{ int ret; /* Enable all media types */
ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE);
/* First reset BMCR */ if (!ret)
ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000); /* Enable Auto Neg */ if (!ret)
ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE); /* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */ if (!ret)
ret = mcs7830_write_phy(dev, MII_BMCR,
BMCR_ANENABLE | BMCR_ANRESTART ); return ret;
}
/* * if we can read register 22, the chip revision is C or higher
*/ staticint mcs7830_get_rev(struct usbnet *dev)
{
u8 dummy[2]; int ret;
ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy); if (ret > 0) return 2; /* Rev C or later */ return 1; /* earlier revision */
}
/* * On rev. C we need to set the pause threshold
*/ staticvoid mcs7830_rev_C_fixup(struct usbnet *dev)
{
u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT; int retry;
staticint mcs7830_apply_base_config(struct usbnet *dev)
{ int ret;
/* re-configure known MAC (suspend case etc.) */
ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr); if (ret) {
dev_info(&dev->udev->dev, "Cannot set MAC address\n"); goto out;
}
/* Set up PHY */
ret = mcs7830_set_autoneg(dev, 0); if (ret) {
dev_info(&dev->udev->dev, "Cannot set autoneg\n"); goto out;
}
ret = usbnet_get_endpoints(dev, udev);
out: return ret;
}
/* The chip always appends a status byte that we need to strip */ staticint mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
u8 status;
/* This check is no longer done by usbnet */ if (skb->len < dev->net->hard_header_len) {
dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); return 0;
}
skb_trim(skb, skb->len - 1);
status = skb->data[skb->len];
if (status != MCS7830_RX_FRAME_CORRECT) {
dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status);
/* hmm, perhaps usbnet.c already sees a globally visible
frame error and increments rx_errors on its own already? */
dev->net->stats.rx_errors++;
if (status & (MCS7830_RX_SHORT_FRAME
|MCS7830_RX_LENGTH_ERROR
|MCS7830_RX_LARGE_FRAME))
dev->net->stats.rx_length_errors++; if (status & MCS7830_RX_ALIGNMENT_ERROR)
dev->net->stats.rx_frame_errors++; if (status & MCS7830_RX_CRC_ERROR)
dev->net->stats.rx_crc_errors++;
}
staticint mcs7830_reset_resume (struct usb_interface *intf)
{ /* YES, this function is successful enough that ethtool -d
does show same output pre-/post-suspend */
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.