if (!port_priv->nr) {
reg = ADIN1110_RX;
ret = adin1110_read_reg(priv, ADIN1110_RX_FSIZE, &frame_size);
} else {
reg = ADIN2111_RX_P2;
ret = adin1110_read_reg(priv, ADIN2111_RX_P2_FSIZE,
&frame_size);
}
if (ret < 0) return ret;
/* The read frame size includes the extra 2 bytes * from the ADIN1110 frame header.
*/ if (frame_size < ADIN1110_FRAME_HEADER_LEN + ADIN1110_FEC_LEN) return -EINVAL;
round_len = adin1110_round_len(frame_size); if (round_len < 0) return -EINVAL;
staticint adin1110_write_fifo(struct adin1110_port_priv *port_priv, struct sk_buff *txb)
{ struct adin1110_priv *priv = port_priv->priv;
u32 header_len = ADIN1110_WR_HEADER_LEN;
__be16 frame_header; int padding = 0; int padded_len; int round_len; int ret;
/* Pad frame to 64 byte length, * MAC nor PHY will otherwise add the * required padding. * The FEC will be added by the MAC internally.
*/ if (txb->len + ADIN1110_FEC_LEN < 64)
padding = 64 - (txb->len + ADIN1110_FEC_LEN);
/* mention the port on which to send the frame in the frame header */
frame_header = cpu_to_be16(port_priv->nr);
memcpy(&priv->data[header_len], &frame_header,
ADIN1110_FRAME_HEADER_LEN);
staticint adin1110_read_mdio_acc(struct adin1110_priv *priv)
{
u32 val; int ret;
mutex_lock(&priv->lock);
ret = adin1110_read_reg(priv, ADIN1110_MDIOACC, &val);
mutex_unlock(&priv->lock); if (ret < 0) return 0;
return val;
}
staticint adin1110_mdio_read(struct mii_bus *bus, int phy_id, int reg)
{ struct adin1110_priv *priv = bus->priv;
u32 val = 0; int ret;
if (mdio_phy_id_is_c45(phy_id)) return -EOPNOTSUPP;
val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_RD);
val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1);
val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id);
val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg);
/* write the clause 22 read command to the chip */
mutex_lock(&priv->lock);
ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val);
mutex_unlock(&priv->lock); if (ret < 0) return ret;
/* ADIN1110_MDIO_TRDONE BIT of the ADIN1110_MDIOACC * register is set when the read is done. * After the transaction is done, ADIN1110_MDIO_DATA * bitfield of ADIN1110_MDIOACC register will contain * the requested register value.
*/
ret = readx_poll_timeout_atomic(adin1110_read_mdio_acc, priv, val,
(val & ADIN1110_MDIO_TRDONE),
100, 30000); if (ret < 0) return ret;
return (val & ADIN1110_MDIO_DATA);
}
staticint adin1110_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 reg_val)
{ struct adin1110_priv *priv = bus->priv;
u32 val = 0; int ret;
if (mdio_phy_id_is_c45(phy_id)) return -EOPNOTSUPP;
val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_WR);
val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1);
val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id);
val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg);
val |= FIELD_PREP(ADIN1110_MDIO_DATA, reg_val);
/* write the clause 22 write command to the chip */
mutex_lock(&priv->lock);
ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val);
mutex_unlock(&priv->lock); if (ret < 0) return ret;
/* ADIN1110 MAC-PHY contains an ADIN1100 PHY. * ADIN2111 MAC-PHY contains two ADIN1100 PHYs. * By registering a new MDIO bus we allow the PAL to discover * the encapsulated PHY and probe the ADIN1100 driver.
*/ staticint adin1110_register_mdiobus(struct adin1110_priv *priv, struct device *dev)
{ struct mii_bus *mii_bus; int ret;
mii_bus = devm_mdiobus_alloc(dev); if (!mii_bus) return -ENOMEM;
while (budget) {
ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1); if (ret < 0) return;
if (!adin1110_port_rx_ready(port_priv, status1)) break;
ret = adin1110_read_fifo(port_priv); if (ret < 0) return;
budget--;
}
}
staticvoid adin1110_wake_queues(struct adin1110_priv *priv)
{ int i;
for (i = 0; i < priv->cfg->ports_nr; i++)
netif_wake_queue(priv->ports[i]->netdev);
}
static irqreturn_t adin1110_irq(int irq, void *p)
{ struct adin1110_priv *priv = p;
u32 status1;
u32 val; int ret; int i;
mutex_lock(&priv->lock);
ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1); if (ret < 0) goto out;
if (priv->append_crc && (status1 & ADIN1110_SPI_ERR))
dev_warn_ratelimited(&priv->spidev->dev, "SPI CRC error on write.\n");
ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val); if (ret < 0) goto out;
/* TX FIFO space is expressed in half-words */
priv->tx_space = 2 * val;
for (i = 0; i < priv->cfg->ports_nr; i++) { if (adin1110_port_rx_ready(priv->ports[i], status1))
adin1110_read_frames(priv->ports[i],
ADIN1110_MAX_FRAMES_READ);
}
if (priv->tx_space > 0 && ret >= 0)
adin1110_wake_queues(priv);
return IRQ_HANDLED;
}
/* ADIN1110 can filter up to 16 MAC addresses, mac_nr here is the slot used */ staticint adin1110_write_mac_address(struct adin1110_port_priv *port_priv, int mac_nr, const u8 *addr,
u8 *mask, u32 port_rules)
{ struct adin1110_priv *priv = port_priv->priv;
u32 offset = mac_nr * 2;
u32 port_rules_mask; int ret;
u32 val;
if (!port_priv->nr)
port_rules_mask = ADIN1110_MAC_ADDR_APPLY2PORT; else
port_rules_mask = ADIN2111_MAC_ADDR_APPLY2PORT2;
if (port_rules & port_rules_mask)
port_rules_mask |= ADIN1110_MAC_ADDR_TO_HOST | ADIN2111_MAC_ADDR_TO_OTHER_PORT;
port_rules_mask |= GENMASK(15, 0);
val = port_rules | get_unaligned_be16(&addr[0]);
ret = adin1110_set_bits(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset,
port_rules_mask, val); if (ret < 0) return ret;
val = get_unaligned_be32(&addr[2]);
ret = adin1110_write_reg(priv,
ADIN1110_MAC_ADDR_FILTER_LWR + offset, val); if (ret < 0) return ret;
/* Only the first two MAC address slots support masking. */ if (mac_nr < ADIN_MAC_P1_ADDR_SLOT) {
val = get_unaligned_be16(&mask[0]);
ret = adin1110_write_reg(priv,
ADIN1110_MAC_ADDR_MASK_UPR + offset,
val); if (ret < 0) return ret;
val = get_unaligned_be32(&mask[2]); return adin1110_write_reg(priv,
ADIN1110_MAC_ADDR_MASK_LWR + offset,
val);
}
return 0;
}
staticint adin1110_clear_mac_address(struct adin1110_priv *priv, int mac_nr)
{
u32 offset = mac_nr * 2; int ret;
ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, 0); if (ret < 0) return ret;
ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + offset, 0); if (ret < 0) return ret;
/* only the first two MAC address slots are maskable */ if (mac_nr <= 1) {
ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_UPR + offset, 0); if (ret < 0) return ret;
ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_LWR + offset, 0);
}
staticbool adin1110_can_offload_forwarding(struct adin1110_priv *priv)
{ int i;
if (priv->cfg->id != ADIN2111_MAC) returnfalse;
/* Can't enable forwarding if ports do not belong to the same bridge */ if (priv->ports[0]->bridge != priv->ports[1]->bridge || !priv->ports[0]->bridge) returnfalse;
/* Can't enable forwarding if there is a port * that has been blocked by STP.
*/ for (i = 0; i < priv->cfg->ports_nr; i++) { if (priv->ports[i]->state != BR_STATE_FORWARDING) returnfalse;
}
/* Configure MAC to compute and append the FCS itself. */
ret = adin1110_write_reg(priv, ADIN1110_CONFIG2, ADIN1110_CRC_APPEND); if (ret < 0) goto out;
val = ADIN1110_TX_RDY_IRQ | ADIN1110_RX_RDY_IRQ | ADIN1110_SPI_ERR_IRQ; if (priv->cfg->id == ADIN2111_MAC)
val |= ADIN2111_RX_RDY_IRQ;
priv->irq_mask = val;
ret = adin1110_write_reg(priv, ADIN1110_IMASK1, ~val); if (ret < 0) {
netdev_err(net_dev, "Failed to enable chip IRQs: %d\n", ret); goto out;
}
ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val); if (ret < 0) {
netdev_err(net_dev, "Failed to read TX FIFO space: %d\n", ret); goto out;
}
priv->tx_space = 2 * val;
port_priv->state = BR_STATE_FORWARDING;
ret = adin1110_set_mac_address(net_dev, net_dev->dev_addr); if (ret < 0) {
netdev_err(net_dev, "Could not set MAC address: %pM, %d\n",
net_dev->dev_addr, ret); goto out;
}
ret = adin1110_set_bits(priv, ADIN1110_CONFIG1, ADIN1110_CONFIG1_SYNC,
ADIN1110_CONFIG1_SYNC);
/* PHY ID is stored in the MAC registers too, * check spi connection by reading it.
*/ staticint adin1110_check_spi(struct adin1110_priv *priv)
{ struct gpio_desc *reset_gpio; int ret;
u32 val;
reset_gpio = devm_gpiod_get_optional(&priv->spidev->dev, "reset",
GPIOD_OUT_LOW); if (reset_gpio) { /* MISO pin is used for internal configuration, can't have * anyone else disturbing the SDO line.
*/
spi_bus_lock(priv->spidev->controller);
/* Need to wait 90 ms before interacting with * the MAC after a HW reset.
*/
fsleep(90000);
spi_bus_unlock(priv->spidev->controller);
}
ret = adin1110_read_reg(priv, ADIN1110_PHY_ID, &val); if (ret < 0) return ret;
if (val != priv->cfg->phy_id_val) {
dev_err(&priv->spidev->dev, "PHY ID expected: %x, read: %x\n",
priv->cfg->phy_id_val, val); return -EIO;
}
return 0;
}
staticint adin1110_hw_forwarding(struct adin1110_priv *priv, bool enable)
{ int ret; int i;
priv->forwarding = enable;
if (!priv->forwarding) { for (i = ADIN_MAC_FDB_ADDR_SLOT; i < ADIN_MAC_MAX_ADDR_SLOTS; i++) {
ret = adin1110_clear_mac_address(priv, i); if (ret < 0) return ret;
}
}
/* Forwarding is optimised when MAC runs in Cut Through mode. */
ret = adin1110_set_bits(priv, ADIN1110_CONFIG2,
ADIN2111_PORT_CUT_THRU_EN,
priv->forwarding ? ADIN2111_PORT_CUT_THRU_EN : 0); if (ret < 0) return ret;
for (i = 0; i < priv->cfg->ports_nr; i++) {
ret = adin1110_setup_rx_mode(priv->ports[i]); if (ret < 0) return ret;
}
if (!adin1110_port_dev_check(dev)) return NOTIFY_DONE;
switch (event) { case NETDEV_CHANGEUPPER: if (netif_is_bridge_master(info->upper_dev)) { if (info->linking)
ret = adin1110_port_bridge_join(port_priv, info->upper_dev); else
ret = adin1110_port_bridge_leave(port_priv, info->upper_dev);
} break; default: break;
}
mutex_lock(&priv->lock);
ret = adin1110_set_mac_address(port_priv->netdev,
port_priv->netdev->dev_addr); if (ret < 0) goto out;
if (adin1110_can_offload_forwarding(priv))
ret = adin1110_hw_forwarding(priv, true); else
ret = adin1110_setup_rx_mode(port_priv);
out:
mutex_unlock(&priv->lock);
mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT;
ret = adin1110_clear_mac_address(priv, mac_slot); if (ret < 0) goto out;
ret = adin1110_hw_forwarding(priv, false); if (ret < 0) goto out;
/* Allow only BPDUs to be passed to the CPU */
eth_broadcast_addr(mask);
port_rules = adin1110_port_rules(port_priv, true, false);
ret = adin1110_write_mac_address(port_priv, mac_slot, mac,
mask, port_rules);
out:
mutex_unlock(&priv->lock);
return ret;
}
/* ADIN1110/2111 does not have any native STP support. * Listen for bridge core state changes and * allow all frames to pass or only the BPDUs.
*/ staticint adin1110_port_attr_stp_state_set(struct adin1110_port_priv *port_priv,
u8 state)
{ switch (state) { case BR_STATE_FORWARDING: return adin1110_port_set_forwarding_state(port_priv); case BR_STATE_LEARNING: case BR_STATE_LISTENING: case BR_STATE_DISABLED: case BR_STATE_BLOCKING: return adin1110_port_set_blocking_state(port_priv); default: return -EINVAL;
}
}
port_priv->phydev = get_phy_device(priv->mii_bus, i + 1, false); if (IS_ERR(port_priv->phydev)) {
netdev_err(netdev, "Could not find PHY with device address: %d.\n", i); return PTR_ERR(port_priv->phydev);
}
port_priv->phydev = phy_connect(netdev,
phydev_name(port_priv->phydev),
adin1110_adjust_link,
PHY_INTERFACE_MODE_INTERNAL); if (IS_ERR(port_priv->phydev)) {
netdev_err(netdev, "Could not connect PHY with device address: %d.\n", i); return PTR_ERR(port_priv->phydev);
}
ret = devm_add_action_or_reset(dev, adin1110_disconnect_phy,
port_priv->phydev); if (ret < 0) return ret;
}
/* ADIN1110 INT_N pin will be used to signal the host */
ret = devm_request_threaded_irq(dev, priv->spidev->irq, NULL,
adin1110_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
dev_name(dev), priv); if (ret < 0) return ret;
for (i = 0; i < priv->cfg->ports_nr; i++) {
ret = devm_register_netdev(dev, priv->ports[i]->netdev); if (ret < 0) {
dev_err(dev, "Failed to register network device.\n"); return ret;
}
}
/* use of CRC on control and data transactions is pin dependent */
priv->append_crc = device_property_read_bool(dev, "adi,spi-crc"); if (priv->append_crc)
crc8_populate_msb(adin1110_crc_table, 0x7);
ret = adin1110_check_spi(priv); if (ret < 0) {
dev_err(dev, "Probe SPI Read check failed: %d\n", ret); return ret;
}
ret = adin1110_write_reg(priv, ADIN1110_RESET, ADIN1110_SWRESET); if (ret < 0) return ret;
ret = adin1110_register_mdiobus(priv, dev); if (ret < 0) {
dev_err(dev, "Could not register MDIO bus %d\n", ret); return ret;
}
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.