/* A single URB buffer must be large enough to hold a complete jumbo packet
*/ #define TX_SS_URB_SIZE (32 * 1024) #define TX_HS_URB_SIZE (16 * 1024) #define TX_FS_URB_SIZE (10 * 1024)
/* use ethtool to change the level for any given device */ staticint msg_level = -1;
module_param(msg_level, int, 0);
MODULE_PARM_DESC(msg_level, "Override default message level");
staticstruct sk_buff *lan78xx_get_buf(struct sk_buff_head *buf_pool)
{ if (skb_queue_empty(buf_pool)) return NULL;
staticint lan78xx_start_tx_path(struct lan78xx_net *dev)
{ int ret;
netif_dbg(dev, drv, dev->net, "start tx path");
/* Start the MAC transmitter */
ret = lan78xx_start_hw(dev, MAC_TX, MAC_TX_TXEN_); if (ret < 0) return ret;
/* Start the Tx FIFO */
ret = lan78xx_start_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_); if (ret < 0) return ret;
return 0;
}
staticint lan78xx_stop_tx_path(struct lan78xx_net *dev)
{ int ret;
netif_dbg(dev, drv, dev->net, "stop tx path");
/* Stop the Tx FIFO */
ret = lan78xx_stop_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_, FCT_TX_CTL_DIS_); if (ret < 0) return ret;
/* Stop the MAC transmitter */
ret = lan78xx_stop_hw(dev, MAC_TX, MAC_TX_TXEN_, MAC_TX_TXD_); if (ret < 0) return ret;
return 0;
}
/* The caller must ensure the Tx path is stopped before calling * lan78xx_flush_tx_fifo().
*/ staticint lan78xx_flush_tx_fifo(struct lan78xx_net *dev)
{ return lan78xx_flush_fifo(dev, FCT_TX_CTL, FCT_TX_CTL_RST_);
}
staticint lan78xx_start_rx_path(struct lan78xx_net *dev)
{ int ret;
netif_dbg(dev, drv, dev->net, "start rx path");
/* Start the Rx FIFO */
ret = lan78xx_start_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_); if (ret < 0) return ret;
/* Start the MAC receiver*/
ret = lan78xx_start_hw(dev, MAC_RX, MAC_RX_RXEN_); if (ret < 0) return ret;
return 0;
}
staticint lan78xx_stop_rx_path(struct lan78xx_net *dev)
{ int ret;
netif_dbg(dev, drv, dev->net, "stop rx path");
/* Stop the MAC receiver */
ret = lan78xx_stop_hw(dev, MAC_RX, MAC_RX_RXEN_, MAC_RX_RXD_); if (ret < 0) return ret;
/* Stop the Rx FIFO */
ret = lan78xx_stop_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_, FCT_RX_CTL_DIS_); if (ret < 0) return ret;
return 0;
}
/* The caller must ensure the Rx path is stopped before calling * lan78xx_flush_rx_fifo().
*/ staticint lan78xx_flush_rx_fifo(struct lan78xx_net *dev)
{ return lan78xx_flush_fifo(dev, FCT_RX_CTL, FCT_RX_CTL_RST_);
}
/* Loop until the read is completed with timeout called with mdiobus_mutex held */ staticint lan78xx_mdiobus_wait_not_busy(struct lan78xx_net *dev)
{ unsignedlong start_time = jiffies;
u32 val; int ret;
do {
ret = lan78xx_read_reg(dev, MII_ACC, &val); if (ret < 0) return ret;
if (!(val & MII_ACC_MII_BUSY_)) return 0;
} while (!time_after(jiffies, start_time + HZ));
return -ETIMEDOUT;
}
staticinline u32 mii_access(int id, int index, int read)
{
u32 ret;
ret = ((u32)id << MII_ACC_PHY_ADDR_SHIFT_) & MII_ACC_PHY_ADDR_MASK_;
ret |= ((u32)index << MII_ACC_MIIRINDA_SHIFT_) & MII_ACC_MIIRINDA_MASK_; if (read)
ret |= MII_ACC_MII_READ_; else
ret |= MII_ACC_MII_WRITE_;
ret |= MII_ACC_MII_BUSY_;
do {
ret = lan78xx_read_reg(dev, E2P_CMD, &val); if (ret < 0) return ret;
if (!(val & E2P_CMD_EPC_BUSY_)) return 0;
usleep_range(40, 100);
} while (!time_after(jiffies, start_time + HZ));
netdev_warn(dev->net, "EEPROM is busy"); return -ETIMEDOUT;
}
staticint lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset,
u32 length, u8 *data)
{
u32 val, saved; int i, ret;
/* depends on chip, some EEPROM pins are muxed with LED function. * disable & restore LED function to access EEPROM.
*/
ret = lan78xx_read_reg(dev, HW_CFG, &val); if (ret < 0) return ret;
saved = val; if (dev->chipid == ID_REV_CHIP_ID_7800_) {
val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_);
ret = lan78xx_write_reg(dev, HW_CFG, val); if (ret < 0) return ret;
}
ret = lan78xx_eeprom_confirm_not_busy(dev); if (ret == -ETIMEDOUT) goto read_raw_eeprom_done; /* If USB fails, there is nothing to do */ if (ret < 0) return ret;
for (i = 0; i < length; i++) {
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
ret = lan78xx_write_reg(dev, E2P_CMD, val); if (ret < 0) return ret;
ret = lan78xx_wait_eeprom(dev); /* Looks like not USB specific error, try to recover */ if (ret == -ETIMEDOUT) goto read_raw_eeprom_done; /* If USB fails, there is nothing to do */ if (ret < 0) return ret;
ret = lan78xx_read_reg(dev, E2P_DATA, &val); if (ret < 0) return ret;
data[i] = val & 0xFF;
offset++;
}
read_raw_eeprom_done: if (dev->chipid == ID_REV_CHIP_ID_7800_) { int rc = lan78xx_write_reg(dev, HW_CFG, saved); /* If USB fails, there is nothing to do */ if (rc < 0) return rc;
} return ret;
}
staticint lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset,
u32 length, u8 *data)
{
u32 val;
u32 saved; int i, ret;
/* depends on chip, some EEPROM pins are muxed with LED function. * disable & restore LED function to access EEPROM.
*/
ret = lan78xx_read_reg(dev, HW_CFG, &val); if (ret < 0) return ret;
saved = val; if (dev->chipid == ID_REV_CHIP_ID_7800_) {
val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_);
ret = lan78xx_write_reg(dev, HW_CFG, val); if (ret < 0) return ret;
}
ret = lan78xx_eeprom_confirm_not_busy(dev); /* Looks like not USB specific error, try to recover */ if (ret == -ETIMEDOUT) goto write_raw_eeprom_done; /* If USB fails, there is nothing to do */ if (ret < 0) return ret;
/* Issue write/erase enable command */
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
ret = lan78xx_write_reg(dev, E2P_CMD, val); if (ret < 0) return ret;
ret = lan78xx_wait_eeprom(dev); /* Looks like not USB specific error, try to recover */ if (ret == -ETIMEDOUT) goto write_raw_eeprom_done; /* If USB fails, there is nothing to do */ if (ret < 0) return ret;
for (i = 0; i < length; i++) { /* Fill data register */
val = data[i];
ret = lan78xx_write_reg(dev, E2P_DATA, val); if (ret < 0) return ret;
/* Send "write" command */
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
ret = lan78xx_write_reg(dev, E2P_CMD, val); if (ret < 0) return ret;
ret = lan78xx_wait_eeprom(dev); /* Looks like not USB specific error, try to recover */ if (ret == -ETIMEDOUT) goto write_raw_eeprom_done; /* If USB fails, there is nothing to do */ if (ret < 0) return ret;
offset++;
}
write_raw_eeprom_done: if (dev->chipid == ID_REV_CHIP_ID_7800_) { int rc = lan78xx_write_reg(dev, HW_CFG, saved); /* If USB fails, there is nothing to do */ if (rc < 0) return rc;
} return ret;
}
/* returns hash bit number for given MAC address */ staticinline u32 lan78xx_hash(char addr[ETH_ALEN])
{ return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff;
}
i = 1;
netdev_for_each_mc_addr(ha, netdev) { /* set first 32 into Perfect Filter */ if (i < 33) {
lan78xx_set_addr_filter(pdata, i, ha->addr);
} else {
u32 bitnum = lan78xx_hash(ha->addr);
/* Resetting the device while there is activity on the MDIO * bus can result in the MAC interface locking up and not * completing register access transactions.
*/
ret = lan78xx_mdiobus_wait_not_busy(dev); if (ret < 0) goto exit_unlock;
ret = lan78xx_read_reg(dev, MAC_CR, &val); if (ret < 0) goto exit_unlock;
val |= MAC_CR_RST_;
ret = lan78xx_write_reg(dev, MAC_CR, val); if (ret < 0) goto exit_unlock;
/* Wait for the reset to complete before allowing any further * MAC register accesses otherwise the MAC may lock up.
*/ do {
ret = lan78xx_read_reg(dev, MAC_CR, &val); if (ret < 0) goto exit_unlock;
if (!(val & MAC_CR_RST_)) {
ret = 0; goto exit_unlock;
}
} while (!time_after(jiffies, start_time + HZ));
ret = -ETIMEDOUT;
exit_unlock:
mutex_unlock(&dev->mdiobus_mutex);
return ret;
}
/** * lan78xx_phy_int_ack - Acknowledge PHY interrupt * @dev: pointer to the LAN78xx device structure * * This function acknowledges the PHY interrupt by setting the * INT_STS_PHY_INT_ bit in the interrupt status register (INT_STS). * * Return: 0 on success or a negative error code on failure.
*/ staticint lan78xx_phy_int_ack(struct lan78xx_net *dev)
{ return lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
}
/* some work can't be done in tasklets, so we use keventd * * NOTE: annoying asymmetry: if it's active, schedule_work() fails, * but tasklet_schedule() doesn't. hope the failure is rare.
*/ staticvoid lan78xx_defer_kevent(struct lan78xx_net *dev, int work)
{
set_bit(work, &dev->flags); if (!schedule_delayed_work(&dev->wq, 0))
netdev_err(dev->net, "kevent %d may have been dropped\n", work);
}
ret = usb_autopm_get_interface(dev->intf); if (ret) return ret;
/* Invalid EEPROM_INDICATOR at offset zero will result in a failure * to load data from EEPROM
*/ if (ee->magic == LAN78XX_EEPROM_MAGIC)
ret = lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data); elseif ((ee->magic == LAN78XX_OTP_MAGIC) &&
(ee->offset == 0) &&
(ee->len == 512) &&
(data[0] == OTP_INDICATOR_1))
ret = lan78xx_write_raw_otp(dev, ee->offset, ee->len, data);
ret = lan78xx_write_reg(dev, RX_ADDRL, addr_lo); if (ret < 0) return ret;
ret = lan78xx_write_reg(dev, RX_ADDRH, addr_hi); if (ret < 0) return ret;
}
ret = lan78xx_write_reg(dev, MAF_LO(0), addr_lo); if (ret < 0) return ret;
ret = lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_); if (ret < 0) return ret;
eth_hw_addr_set(dev->net, addr);
return 0;
}
/* MDIO read and write wrappers for phylib */ staticint lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx)
{ struct lan78xx_net *dev = bus->priv;
u32 val, addr; int ret;
ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret;
mutex_lock(&dev->mdiobus_mutex);
/* confirm MII not busy */
ret = lan78xx_mdiobus_wait_not_busy(dev); if (ret < 0) goto done;
/* set the address, index & direction (read from PHY) */
addr = mii_access(phy_id, idx, MII_READ);
ret = lan78xx_write_reg(dev, MII_ACC, addr); if (ret < 0) goto done;
ret = lan78xx_mdiobus_wait_not_busy(dev); if (ret < 0) goto done;
ret = lan78xx_read_reg(dev, MII_DATA, &val); if (ret < 0) goto done;
staticint lan78xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx,
u16 regval)
{ struct lan78xx_net *dev = bus->priv;
u32 val, addr; int ret;
ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret;
mutex_lock(&dev->mdiobus_mutex);
/* confirm MII not busy */
ret = lan78xx_mdiobus_wait_not_busy(dev); if (ret < 0) goto done;
val = (u32)regval;
ret = lan78xx_write_reg(dev, MII_DATA, val); if (ret < 0) goto done;
/* set the address, index & direction (write to PHY) */
addr = mii_access(phy_id, idx, MII_WRITE);
ret = lan78xx_write_reg(dev, MII_ACC, addr); if (ret < 0) goto done;
ret = lan78xx_mdiobus_wait_not_busy(dev); if (ret < 0) goto done;
/* call register access here because irq_bus_lock & irq_bus_sync_unlock * are only two callbacks executed in non-atomic contex.
*/
ret = lan78xx_read_reg(dev, INT_EP_CTL, &buf); if (ret < 0) goto irq_bus_sync_unlock;
if (buf != data->irqenable)
ret = lan78xx_write_reg(dev, INT_EP_CTL, data->irqenable);
irq_bus_sync_unlock: if (ret < 0)
netdev_err(dev->net, "Failed to sync IRQ enable register: %pe\n",
ERR_PTR(ret));
/* MAC reset will not de-assert TXEN/RXEN, we need to stop them * manually before reset. TX and RX should be disabled before running * link_up sequence.
*/
ret = lan78xx_stop_tx_path(dev); if (ret < 0) goto link_down_fail;
ret = lan78xx_stop_rx_path(dev); if (ret < 0) goto link_down_fail;
/* MAC reset seems to not affect MAC configuration, no idea if it is * really needed, but it was done in previous driver version. So, leave * it here.
*/
ret = lan78xx_mac_reset(dev); if (ret < 0) goto link_down_fail;
return;
link_down_fail:
netdev_err(dev->net, "Failed to set MAC down with error %pe\n",
ERR_PTR(ret));
}
/** * lan78xx_configure_usb - Configure USB link power settings * @dev: pointer to the LAN78xx device structure * @speed: negotiated Ethernet link speed (in Mbps) * * This function configures U1/U2 link power management for SuperSpeed * USB devices based on the current Ethernet link speed. It uses the * USB_CFG1 register to enable or disable U1 and U2 low-power states. * * Note: Only LAN7800 and LAN7801 support SuperSpeed (USB 3.x). * LAN7850 is a High-Speed-only (USB 2.0) device and is skipped. * * Return: 0 on success or a negative error code on failure.
*/ staticint lan78xx_configure_usb(struct lan78xx_net *dev, int speed)
{
u32 mask, val; int ret;
/* Only configure USB settings for SuperSpeed devices */ if (dev->udev->speed != USB_SPEED_SUPER) return 0;
/* LAN7850 does not support USB 3.x */ if (dev->chipid == ID_REV_CHIP_ID_7850_) {
netdev_warn_once(dev->net, "Unexpected SuperSpeed for LAN7850 (USB 2.0 only)\n"); return 0;
}
switch (speed) { case SPEED_1000: /* Disable U2, enable U1 */
ret = lan78xx_update_reg(dev, USB_CFG1,
USB_CFG1_DEV_U2_INIT_EN_, 0); if (ret < 0) return ret;
case SPEED_100: case SPEED_10: /* Enable both U1 and U2 */
mask = USB_CFG1_DEV_U1_INIT_EN_ | USB_CFG1_DEV_U2_INIT_EN_;
val = mask; return lan78xx_update_reg(dev, USB_CFG1, mask, val);
/** * lan78xx_configure_flowcontrol - Set MAC and FIFO flow control configuration * @dev: pointer to the LAN78xx device structure * @tx_pause: enable transmission of pause frames * @rx_pause: enable reception of pause frames * * This function configures the LAN78xx flow control settings by writing * to the FLOW and FCT_FLOW registers. The pause time is set to the * maximum allowed value (65535 quanta). FIFO thresholds are selected * based on USB speed. * * The Pause Time field is measured in units of 512-bit times (quanta): * - At 1 Gbps: 1 quanta = 512 ns → max ~33.6 ms pause * - At 100 Mbps: 1 quanta = 5.12 µs → max ~335 ms pause * - At 10 Mbps: 1 quanta = 51.2 µs → max ~3.3 s pause * * Flow control thresholds (FCT_FLOW) are used to trigger pause/resume: * - RXUSED is the number of bytes used in the RX FIFO * - Flow is turned ON when RXUSED ≥ FLOW_ON threshold * - Flow is turned OFF when RXUSED ≤ FLOW_OFF threshold * - Both thresholds are encoded in units of 512 bytes (rounded up) * * Thresholds differ by USB speed because available USB bandwidth * affects how fast packets can be drained from the RX FIFO: * - USB 3.x (SuperSpeed): * FLOW_ON = 9216 bytes → 18 units * FLOW_OFF = 4096 bytes → 8 units * - USB 2.0 (High-Speed): * FLOW_ON = 8704 bytes → 17 units * FLOW_OFF = 1024 bytes → 2 units * * Note: The FCT_FLOW register must be configured before enabling TX pause * (i.e., before setting FLOW_CR_TX_FCEN_), as required by the hardware. * * Return: 0 on success or a negative error code on failure.
*/ staticint lan78xx_configure_flowcontrol(struct lan78xx_net *dev, bool tx_pause, bool rx_pause)
{ /* Use maximum pause time: 65535 quanta (512-bit times) */ const u32 pause_time_quanta = 65535;
u32 fct_flow = 0;
u32 flow = 0; int ret;
/* Prepare MAC flow control bits */ if (tx_pause)
flow |= FLOW_CR_TX_FCEN_ | pause_time_quanta;
if (rx_pause)
flow |= FLOW_CR_RX_FCEN_;
/* Select RX FIFO thresholds based on USB speed * * FCT_FLOW layout: * bits [6:0] FLOW_ON threshold (RXUSED ≥ ON → assert pause) * bits [14:8] FLOW_OFF threshold (RXUSED ≤ OFF → deassert pause) * thresholds are expressed in units of 512 bytes
*/ switch (dev->udev->speed) { case USB_SPEED_SUPER:
fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_SS, FLOW_OFF_SS); break; case USB_SPEED_HIGH:
fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_HS, FLOW_OFF_HS); break; default:
netdev_warn(dev->net, "Unsupported USB speed: %d\n",
dev->udev->speed); return -EINVAL;
}
/* Step 1: Write FIFO thresholds before enabling pause frames */
ret = lan78xx_write_reg(dev, FCT_FLOW, fct_flow); if (ret < 0) return ret;
switch (speed) { case SPEED_1000:
mac_cr |= MAC_CR_SPEED_1000_; break; case SPEED_100:
mac_cr |= MAC_CR_SPEED_100_; break; case SPEED_10:
mac_cr |= MAC_CR_SPEED_10_; break; default:
netdev_err(dev->net, "Unsupported speed %d\n", speed); return;
}
if (duplex == DUPLEX_FULL)
mac_cr |= MAC_CR_FULL_DUPLEX_;
/* make sure TXEN and RXEN are disabled before reconfiguring MAC */
ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_SPEED_MASK_ |
MAC_CR_FULL_DUPLEX_ | MAC_CR_EEE_EN_, mac_cr); if (ret < 0) goto link_up_fail;
ret = lan78xx_configure_flowcontrol(dev, tx_pause, rx_pause); if (ret < 0) goto link_up_fail;
ret = lan78xx_configure_usb(dev, speed); if (ret < 0) goto link_up_fail;
lan78xx_rx_urb_submit_all(dev);
ret = lan78xx_flush_rx_fifo(dev); if (ret < 0) goto link_up_fail;
ret = lan78xx_flush_tx_fifo(dev); if (ret < 0) goto link_up_fail;
ret = lan78xx_start_tx_path(dev); if (ret < 0) goto link_up_fail;
ret = lan78xx_start_rx_path(dev); if (ret < 0) goto link_up_fail;
netif_start_queue(net);
return;
link_up_fail:
netdev_err(dev->net, "Failed to set MAC up with error %pe\n",
ERR_PTR(ret));
}
/** * lan78xx_mac_eee_enable - Enable or disable MAC-side EEE support * @dev: LAN78xx device * @enable: true to enable EEE, false to disable * * This function sets or clears the MAC_CR_EEE_EN_ bit to control Energy * Efficient Ethernet (EEE) operation. According to current understanding * of the LAN7800 documentation, this bit can be modified while TX and RX * are enabled. No explicit requirement was found to disable data paths * before changing this bit. * * Return: 0 on success or a negative error code
*/ staticint lan78xx_mac_eee_enable(struct lan78xx_net *dev, bool enable)
{
u32 mac_cr = 0;
/* Software should only change this field when Energy Efficient * Ethernet Enable (EEEEN) is cleared. We ensure that by clearing * EEEEN during probe, and phylink itself guarantees that * mac_disable_tx_lpi() will have been previously called.
*/
ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, timer); if (ret < 0) return ret;
/** * lan78xx_set_fixed_link() - Set fixed link configuration for LAN7801 * @dev: LAN78xx device * * Use fixed link configuration with 1 Gbps full duplex. This is used in special * cases like EVB-KSZ9897-1, where LAN7801 acts as a USB-to-Ethernet interface * to a switch without a visible PHY. * * Return: pointer to the registered fixed PHY, or ERR_PTR() on error.
*/ staticint lan78xx_set_fixed_link(struct lan78xx_net *dev)
{ staticconststruct phylink_link_state state = {
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
};
netdev_info(dev->net, "No PHY found on LAN7801 – using fixed link instead (e.g. EVB-KSZ9897-1)\n");
/** * lan78xx_get_phy() - Probe or register PHY device and set interface mode * @dev: LAN78xx device structure * * This function attempts to find a PHY on the MDIO bus. If no PHY is found * and the chip is LAN7801, it registers a fixed PHY as fallback. It also * sets dev->interface based on chip ID and detected PHY type. * * Return: a valid PHY device pointer, or ERR_PTR() on failure.
*/ staticstruct phy_device *lan78xx_get_phy(struct lan78xx_net *dev)
{ struct phy_device *phydev;
/* Attempt to locate a PHY on the MDIO bus */
phydev = phy_find_first(dev->mdiobus);
switch (dev->chipid) { case ID_REV_CHIP_ID_7801_:
--> --------------------
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.