if (of_property_read_u32(of_node, "vsc8531,vddmac", &vdd))
vdd = MSCC_VDDMAC_3300;
if (of_property_read_u32(of_node, "vsc8531,edge-slowdown", &sd))
sd = 0;
for (i = 0; i < ARRAY_SIZE(edge_table); i++) if (edge_table[i].vddmac == vdd) for (j = 0; j < sd_array_size; j++) if (edge_table[i].slowdown[j] == sd) return (sd_array_size - j - 1);
mutex_lock(&phydev->lock);
reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
reg_val &= ~(MAC_IF_SELECTION_MASK); switch (interface) { case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII:
reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS); break; case PHY_INTERFACE_MODE_RMII:
reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS); break; case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_GMII:
reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS); break; default:
rc = -EINVAL; goto out_unlock;
}
rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val); if (rc) goto out_unlock;
rc = genphy_soft_reset(phydev);
out_unlock:
mutex_unlock(&phydev->lock);
return rc;
}
/* Set the RGMII RX and TX clock skews individually, according to the PHY * interface type, to: * * 0.2 ns (their default, and lowest, hardware value) if delays should * not be enabled * * 2.0 ns (which causes the data to be sampled at exactly half way between * clock transitions at 1000 Mbps) if delays should be enabled
*/ staticint vsc85xx_update_rgmii_cntl(struct phy_device *phydev, u32 rgmii_cntl,
u16 rgmii_rx_delay_mask,
u16 rgmii_tx_delay_mask)
{
u16 rgmii_rx_delay_pos = ffs(rgmii_rx_delay_mask) - 1;
u16 rgmii_tx_delay_pos = ffs(rgmii_tx_delay_mask) - 1; int delay_size = ARRAY_SIZE(vsc85xx_internal_delay);
u16 reg_val = 0;
u16 mask = 0;
s32 rx_delay;
s32 tx_delay; int rc = 0;
/* For traffic to pass, the VSC8502 family needs the RX_CLK disable bit * to be unset for all PHY modes, so do that as part of the paged * register modification. * For some family members (like VSC8530/31/40/41) this bit is reserved * and read-only, and the RX clock is enabled by default.
*/ if (rgmii_cntl == VSC8502_RGMII_CNTL)
mask |= VSC8502_RGMII_RX_CLK_DISABLE;
if (phy_interface_is_rgmii(phydev))
mask |= rgmii_rx_delay_mask | rgmii_tx_delay_mask;
/* mdiobus lock should be locked when using this function */ staticvoid vsc85xx_tr_write(struct phy_device *phydev, u16 addr, u32 val)
{
__phy_write(phydev, MSCC_PHY_TR_MSB, val >> 16);
__phy_write(phydev, MSCC_PHY_TR_LSB, val & GENMASK(15, 0));
__phy_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(addr));
}
/* phydev->bus->mdio_lock should be locked when using this function */ int phy_base_write(struct phy_device *phydev, u32 regnum, u16 val)
{ if (unlikely(!mutex_is_locked(&phydev->mdio.bus->mdio_lock))) {
dev_err(&phydev->mdio.dev, "MDIO bus lock not held!\n");
dump_stack();
}
/* phydev->bus->mdio_lock should be locked when using this function */ int phy_base_read(struct phy_device *phydev, u32 regnum)
{ if (unlikely(!mutex_is_locked(&phydev->mdio.bus->mdio_lock))) {
dev_err(&phydev->mdio.dev, "MDIO bus lock not held!\n");
dump_stack();
}
/* CSR registers are grouped under different Target IDs. * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and * MSCC_EXT_PAGE_CSR_CNTL_19 registers. * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19.
*/
/* Setup the Target ID */
phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20,
MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2));
/* CSR registers are grouped under different Target IDs. * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and * MSCC_EXT_PAGE_CSR_CNTL_19 registers. * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19.
*/
/* Setup the Target ID */
phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20,
MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2));
/* Write the Least Significant Word (LSW) (17) */
phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_17, (u16)val);
/* Write the Most Significant Word (MSW) (18) */
phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_18, (u16)(val >> 16));
/* bus->mdio_lock should be locked when using this function */ staticvoid vsc8584_csr_write(struct phy_device *phydev, u16 addr, u32 val)
{
phy_base_write(phydev, MSCC_PHY_TR_MSB, val >> 16);
phy_base_write(phydev, MSCC_PHY_TR_LSB, val & GENMASK(15, 0));
phy_base_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(addr));
}
/* bus->mdio_lock should be locked when using this function */ int vsc8584_cmd(struct phy_device *phydev, u16 val)
{ unsignedlong deadline;
u16 reg_val;
if (reg_val & PROC_CMD_NCOMPLETED) return -ETIMEDOUT;
return 0;
}
/* bus->mdio_lock should be locked when using this function */ staticint vsc8584_micro_deassert_reset(struct phy_device *phydev, bool patch_en)
{
u32 enable, release;
/* bus->mdio_lock should be locked when using this function */ staticint vsc8584_get_fw_crc(struct phy_device *phydev, u16 start, u16 size,
u16 *crc)
{ int ret;
/* bus->mdio_lock should be locked when using this function */ staticint vsc8584_patch_fw(struct phy_device *phydev, conststruct firmware *fw)
{ int i, ret;
ret = vsc8584_micro_assert_reset(phydev); if (ret) {
dev_err(&phydev->mdio.dev, "%s: failed to assert reset of micro\n", __func__); return ret;
}
/* all writes below are broadcasted to all PHYs in the same package */
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
reg |= SMI_BROADCAST_WR_EN;
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
phy_base_write(phydev, MII_VSC85XX_INT_MASK, 0);
/* The below register writes are tweaking analog and electrical * configuration that were determined through characterization by PHY * engineers. These don't mean anything more than "these are the best * values".
*/
phy_base_write(phydev, MSCC_PHY_EXT_PHY_CNTL_2, 0x0040);
/* end of write broadcasting */
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
reg &= ~SMI_BROADCAST_WR_EN;
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
ret = request_firmware(&fw, MSCC_VSC8574_REVB_INT8051_FW, dev); if (ret) {
dev_err(dev, "failed to load firmware %s, ret: %d\n",
MSCC_VSC8574_REVB_INT8051_FW, ret); return ret;
}
/* Add one byte to size for the one added by the patch_fw function */
ret = vsc8584_get_fw_crc(phydev,
MSCC_VSC8574_REVB_INT8051_FW_START_ADDR,
fw->size + 1, &crc); if (ret) goto out;
if (crc == MSCC_VSC8574_REVB_INT8051_FW_CRC) {
serdes_init = vsc8574_is_serdes_init(phydev);
if (!serdes_init) {
ret = vsc8584_micro_assert_reset(phydev); if (ret) {
dev_err(dev, "%s: failed to assert reset of micro\n",
__func__); goto out;
}
}
} else {
dev_dbg(dev, "FW CRC is not the expected one, patching FW\n");
serdes_init = false;
if (vsc8584_patch_fw(phydev, fw))
dev_warn(dev, "failed to patch FW, expect non-optimal device\n");
}
if (!serdes_init) {
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_EXTENDED_GPIO);
/* Add one byte to size for the one added by the patch_fw * function
*/
ret = vsc8584_get_fw_crc(phydev,
MSCC_VSC8574_REVB_INT8051_FW_START_ADDR,
fw->size + 1, &crc); if (ret) goto out;
if (crc != MSCC_VSC8574_REVB_INT8051_FW_CRC)
dev_warn(dev, "FW CRC after patching is not the expected one, expect non-optimal device\n");
}
/* all writes below are broadcasted to all PHYs in the same package */
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
reg |= SMI_BROADCAST_WR_EN;
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
/* The below register writes are tweaking analog and electrical * configuration that were determined through characterization by PHY * engineers. These don't mean anything more than "these are the best * values".
*/
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_3);
/* end of write broadcasting */
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
reg &= ~SMI_BROADCAST_WR_EN;
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
ret = request_firmware(&fw, MSCC_VSC8584_REVB_INT8051_FW, dev); if (ret) {
dev_err(dev, "failed to load firmware %s, ret: %d\n",
MSCC_VSC8584_REVB_INT8051_FW, ret); return ret;
}
/* Add one byte to size for the one added by the patch_fw function */
ret = vsc8584_get_fw_crc(phydev,
MSCC_VSC8584_REVB_INT8051_FW_START_ADDR,
fw->size + 1, &crc); if (ret) goto out;
if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC) {
dev_dbg(dev, "FW CRC is not the expected one, patching FW\n"); if (vsc8584_patch_fw(phydev, fw))
dev_warn(dev, "failed to patch FW, expect non-optimal device\n");
}
vsc8584_micro_deassert_reset(phydev, false);
/* Add one byte to size for the one added by the patch_fw function */
ret = vsc8584_get_fw_crc(phydev,
MSCC_VSC8584_REVB_INT8051_FW_START_ADDR,
fw->size + 1, &crc); if (ret) goto out;
if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC)
dev_warn(dev, "FW CRC after patching is not the expected one, expect non-optimal device\n");
ret = vsc8584_micro_assert_reset(phydev); if (ret) goto out;
/* Write patch vector 0, to skip IB cal polling */
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_GPIO);
reg = MSCC_ROM_TRAP_SERDES_6G_CFG; /* ROM address to trap, for patch vector 0 */
ret = phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), reg); if (ret) goto out;
reg = MSCC_RAM_TRAP_SERDES_6G_CFG; /* RAM address to jump to, when patch vector 0 enabled */
ret = phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), reg); if (ret) goto out;
reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL);
reg |= PATCH_VEC_ZERO_EN; /* bit 8, enable patch vector 0 */
ret = phy_base_write(phydev, MSCC_INT_MEM_CNTL, reg); if (ret) goto out;
/* In the package, there are two pairs of PHYs (PHY0 + PHY2 and * PHY1 + PHY3). The first PHY of each pair (PHY0 and PHY1) is * the base PHY for timestamping operations.
*/
vsc8531->ts_base_addr = phydev->mdio.addr;
vsc8531->ts_base_phy = addr;
staticvoid vsc85xx_coma_mode_release(struct phy_device *phydev)
{ /* The coma mode (pin or reg) provides an optional feature that * may be used to control when the PHYs become active. * Alternatively the COMA_MODE pin may be connected low * so that the PHYs are fully active once out of reset.
*/
/* Enable output (mode=0) and write zero to it */
vsc85xx_phy_write_page(phydev, MSCC_PHY_PAGE_EXTENDED_GPIO);
__phy_modify(phydev, MSCC_PHY_GPIO_CONTROL_2,
MSCC_PHY_COMA_MODE | MSCC_PHY_COMA_OUTPUT, 0);
vsc85xx_phy_write_page(phydev, MSCC_PHY_PAGE_STANDARD);
}
ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_EXTENDED_GPIO); if (ret) return ret;
val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK);
val &= ~MAC_CFG_MASK; if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) {
val |= MAC_CFG_QSGMII;
} elseif (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
val |= MAC_CFG_SGMII;
} else {
ret = -EINVAL; return ret;
}
ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); if (ret) return ret;
ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_STANDARD); if (ret) return ret;
val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT |
PROC_CMD_READ_MOD_WRITE_PORT; if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
val |= PROC_CMD_QSGMII_MAC; else
val |= PROC_CMD_SGMII_MAC;
ret = vsc8584_cmd(phydev, val); if (ret) return ret;
usleep_range(10000, 20000);
/* Disable SerDes for 100Base-FX */
ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF |
PROC_CMD_FIBER_PORT(vsc8531->addr) |
PROC_CMD_FIBER_DISABLE |
PROC_CMD_READ_MOD_WRITE_PORT |
PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX); if (ret) return ret;
/* Disable SerDes for 1000Base-X */
ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF |
PROC_CMD_FIBER_PORT(vsc8531->addr) |
PROC_CMD_FIBER_DISABLE |
PROC_CMD_READ_MOD_WRITE_PORT |
PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X); if (ret) return ret;
ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_EXTENDED_GPIO); if (ret) return ret;
val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK);
val &= ~MAC_CFG_MASK; if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) {
val |= MAC_CFG_QSGMII;
} elseif (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
val |= MAC_CFG_SGMII;
} elseif (phy_interface_is_rgmii(phydev)) {
val |= MAC_CFG_RGMII;
} else {
ret = -EINVAL; return ret;
}
ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); if (ret) return ret;
ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_STANDARD); if (ret) return ret;
if (!phy_interface_is_rgmii(phydev)) {
val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT |
PROC_CMD_READ_MOD_WRITE_PORT; if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
val |= PROC_CMD_QSGMII_MAC; else
val |= PROC_CMD_SGMII_MAC;
ret = vsc8584_cmd(phydev, val); if (ret) return ret;
usleep_range(10000, 20000);
}
/* Disable SerDes for 100Base-FX */
ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF |
PROC_CMD_FIBER_PORT(vsc8531->addr) |
PROC_CMD_FIBER_DISABLE |
PROC_CMD_READ_MOD_WRITE_PORT |
PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX); if (ret) return ret;
/* Some parts of the init sequence are identical for every PHY in the * package. Some parts are modifying the GPIO register bank which is a * set of registers that are affecting all PHYs, a few resetting the * microprocessor common to all PHYs. The CRC check responsible of the * checking the firmware within the 8051 microprocessor can only be * accessed via the PHY whose internal address in the package is 0. * All PHYs' interrupts mask register has to be zeroed before enabling * any PHY's interrupt in this register. * For all these reasons, we need to do the init sequence once and only * once whatever is the first PHY in the package that is initialized and * do the correct init sequence for all PHYs that are package-critical * in this pre-init function.
*/ if (phy_package_init_once(phydev)) { /* The following switch statement assumes that the lowest * nibble of the phy_id_mask is always 0. This works because * the lowest nibble of the PHY_ID's below are also 0.
*/
WARN_ON(phydev->drv->phy_id_mask & 0xf);
switch (phydev->phy_id & phydev->drv->phy_id_mask) { case PHY_ID_VSC8504: case PHY_ID_VSC8552: case PHY_ID_VSC8572: case PHY_ID_VSC8574:
ret = vsc8574_config_pre_init(phydev); if (ret) goto err;
ret = vsc8574_config_host_serdes(phydev); if (ret) goto err; break; case PHY_ID_VSC856X: case PHY_ID_VSC8575: case PHY_ID_VSC8582: case PHY_ID_VSC8584:
ret = vsc8584_config_pre_init(phydev); if (ret) goto err;
ret = vsc8584_config_host_serdes(phydev); if (ret) goto err;
vsc85xx_coma_mode_release(phydev); break; default:
ret = -EINVAL; break;
}
if (ret) goto err;
}
phy_unlock_mdio_bus(phydev);
ret = vsc8584_macsec_init(phydev); if (ret) return ret;
ret = vsc8584_ptp_init(phydev); if (ret) return ret;
val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
val &= ~(MEDIA_OP_MODE_MASK | VSC8584_MAC_IF_SELECTION_MASK);
val |= (MEDIA_OP_MODE_COPPER << MEDIA_OP_MODE_POS) |
(VSC8584_MAC_IF_SELECTION_SGMII << VSC8584_MAC_IF_SELECTION_POS);
ret = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, val); if (ret) return ret;
ret = vsc85xx_update_rgmii_cntl(phydev, VSC8572_RGMII_CNTL,
VSC8572_RGMII_RX_DELAY_MASK,
VSC8572_RGMII_TX_DELAY_MASK); if (ret) return ret;
ret = genphy_soft_reset(phydev); if (ret) return ret;
for (i = 0; i < vsc8531->nleds; i++) {
ret = vsc85xx_led_cntl_set(phydev, i, vsc8531->leds_mode[i]); if (ret) return ret;
}
return 0;
err:
phy_unlock_mdio_bus(phydev); return ret;
}
static irqreturn_t vsc8584_handle_interrupt(struct phy_device *phydev)
{
irqreturn_t ret; int irq_status;
irq_status = phy_read(phydev, MII_VSC85XX_INT_STATUS); if (irq_status < 0) return IRQ_NONE;
/* Timestamping IRQ does not set a bit in the global INT_STATUS, so * irq_status would be 0.
*/
ret = vsc8584_handle_ts_interrupt(phydev); if (!(irq_status & MII_VSC85XX_INT_MASK_MASK)) return ret;
if (irq_status & MII_VSC85XX_INT_MASK_EXT)
vsc8584_handle_macsec_interrupt(phydev);
if (irq_status & MII_VSC85XX_INT_MASK_LINK_CHG)
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
staticint vsc85xx_config_init(struct phy_device *phydev)
{ int rc, i, phy_id; struct vsc8531_private *vsc8531 = phydev->priv;
rc = vsc85xx_default_config(phydev); if (rc) return rc;
rc = vsc85xx_mac_if_set(phydev, phydev->interface); if (rc) return rc;
rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic); if (rc) return rc;
ret = vsc85xx_csr_write(phydev, PHY_MCB_TARGET, reg,
op | (1 << mcb)); if (ret) return -EINVAL;
deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); do {
usleep_range(500, 1000);
val = vsc85xx_csr_read(phydev, PHY_MCB_TARGET, reg);
if (val == 0xffffffff) return -EIO;
} while (time_before(jiffies, deadline) && (val & op));
if (val & op) return -ETIMEDOUT;
return 0;
}
/* Trigger a read to the specified MCB */ int phy_update_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb)
{ return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_READ);
}
/* Trigger a write to the specified MCB */ int phy_commit_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb)
{ return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_WRITE);
}
staticint vsc8514_config_host_serdes(struct phy_device *phydev)
{ int ret;
u16 val;
ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_EXTENDED_GPIO); if (ret) return ret;
val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK);
val &= ~MAC_CFG_MASK;
val |= MAC_CFG_QSGMII;
ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); if (ret) return ret;
ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_STANDARD); if (ret) return ret;
ret = vsc8584_cmd(phydev, PROC_CMD_NOP); if (ret) return ret;
/* all writes below are broadcasted to all PHYs in the same package */
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
reg |= SMI_BROADCAST_WR_EN;
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
/* Add pre-patching commands to: * 1. enable 8051 clock, operate 8051 clock at 125 MHz * instead of HW default 62.5MHz * 2. write patch vector 0, to skip IB cal polling executed * as part of the 0x80E0 ROM command
*/
vsc8584_micro_deassert_reset(phydev, false);
vsc8584_micro_assert_reset(phydev);
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_EXTENDED_GPIO); /* ROM address to trap, for patch vector 0 */
reg = MSCC_ROM_TRAP_SERDES_6G_CFG;
ret = phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), reg); if (ret) goto err; /* RAM address to jump to, when patch vector 0 enabled */
reg = MSCC_RAM_TRAP_SERDES_6G_CFG;
ret = phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), reg); if (ret) goto err;
reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL);
reg |= PATCH_VEC_ZERO_EN; /* bit 8, enable patch vector 0 */
ret = phy_base_write(phydev, MSCC_INT_MEM_CNTL, reg); if (ret) goto err;
/* Some parts of the init sequence are identical for every PHY in the * package. Some parts are modifying the GPIO register bank which is a * set of registers that are affecting all PHYs, a few resetting the * microprocessor common to all PHYs. * All PHYs' interrupts mask register has to be zeroed before enabling * any PHY's interrupt in this register. * For all these reasons, we need to do the init sequence once and only * once whatever is the first PHY in the package that is initialized and * do the correct init sequence for all PHYs that are package-critical * in this pre-init function.
*/ if (phy_package_init_once(phydev)) {
ret = vsc8514_config_pre_init(phydev); if (ret) goto err;
ret = vsc8514_config_host_serdes(phydev); if (ret) goto err;
vsc85xx_coma_mode_release(phydev);
}
phy_unlock_mdio_bus(phydev);
ret = phy_modify(phydev, MSCC_PHY_EXT_PHY_CNTL_1, MEDIA_OP_MODE_MASK,
MEDIA_OP_MODE_COPPER << MEDIA_OP_MODE_POS);
if (ret) return ret;
ret = genphy_soft_reset(phydev);
if (ret) return ret;
for (i = 0; i < vsc8531->nleds; i++) {
ret = vsc85xx_led_cntl_set(phydev, i, vsc8531->leds_mode[i]); if (ret) return ret;
}
return ret;
err:
phy_unlock_mdio_bus(phydev); return ret;
}
staticint vsc85xx_ack_interrupt(struct phy_device *phydev)
{ int rc = 0;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
return (rc < 0) ? rc : 0;
}
staticint vsc85xx_config_intr(struct phy_device *phydev)
{ int rc;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
rc = vsc85xx_ack_interrupt(phydev); if (rc) return rc;
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.