#define IPC_CMD_NOOP 0x0 /* Do nothing */ #define IPC_CMD_INFO 0x1 /* Get Firmware Version */ #define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */ #define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */ #define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */ #define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */ #define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */ #define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */ #define IPC_CMD_SET_LED 0x23 /* Set led */
/* The IPC sync by using a single parity bit. * Each CMD have alternately this bit set or clear * to understand correct flow and packet order.
*/
curr_parity = priv->parity_status; if (priv->parity_status)
cmd |= AEON_IPC_CMD_PARITY;
/* Always update parity for next packet */
priv->parity_status = !priv->parity_status;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd); if (ret) return ret;
/* Wait for packet to be processed */
usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000);
/* With no ret_sts, ignore waiting for packet completion * (ipc parity bit sync)
*/ if (!ret_sts) return 0;
ret = aeon_ipc_wait_cmd(phydev, curr_parity); if (ret) return ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS); if (ret < 0) return ret;
/* If data is NULL, return 0 or negative error. * If data not NULL, return number of Bytes received from IPC or * a negative error.
*/ staticint aeon_ipc_send_msg(struct phy_device *phydev,
u16 opcode, u16 *data, unsignedint data_len,
u16 *ret_data)
{ struct as21xxx_priv *priv = phydev->priv; unsignedint ret_size;
u16 cmd, ret_sts; int ret; int i;
/* IPC have a max of 8 register to transfer data, * make sure we never exceed this.
*/ if (data_len > AEON_IPC_DATA_MAX) return -EINVAL;
for (i = 0; i < data_len / sizeof(u16); i++)
phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
data[i]);
ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts); if (ret) {
phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
opcode, ret); goto out;
}
if (!data) goto out;
if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) {
ret = -EINVAL; goto out;
}
/* Prevent IPC from stack smashing the kernel. * We can't trust IPC to return a good value and we always * preallocate space for 16 Bytes.
*/
ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts); if (ret_size > AEON_IPC_DATA_MAX) {
ret = -EINVAL; goto out;
}
/* Read data from IPC data register for ret_size value from IPC */ for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) {
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i)); if (ret < 0) goto out;
/* Logic to sync parity bit with IPC. * We send 2 NOP cmd with same partity and we wait for IPC * to handle the packet only for the second one. This way * we make sure we are sync for every next cmd.
*/ staticint aeon_ipc_sync_parity(struct phy_device *phydev, struct as21xxx_priv *priv)
{
u16 ret_sts; int ret;
mutex_lock(&priv->ipc_lock);
/* Send NOP with no parity */
aeon_ipc_noop(phydev, priv, NULL);
ret = devm_mutex_init(&phydev->mdio.dev,
&priv->ipc_lock); if (ret) return ret;
ret = aeon_ipc_sync_parity(phydev, priv); if (ret) return ret;
ret = aeon_ipc_get_fw_version(phydev); if (ret) return ret;
/* Enable PTP clk if not already Enabled */
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK,
VEND1_PTP_CLK_EN); if (ret) return ret;
return aeon_dpc_ra_enable(phydev);
}
staticint as21xxx_read_link(struct phy_device *phydev, int *bmcr)
{ int status;
/* Normal C22 BMCR report inconsistent data, use * the mapped C22 in C45 to have more consistent link info.
*/
*bmcr = phy_read_mmd(phydev, MDIO_MMD_AN,
AS21XXX_MDIO_AN_C22 + MII_BMCR); if (*bmcr < 0) return *bmcr;
/* Autoneg is being started, therefore disregard current * link status and report link as down.
*/ if (*bmcr & BMCR_ANRESTART) {
phydev->link = 0; return 0;
}
status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); if (status < 0) return status;
phydev->link = !!(status & MDIO_STAT1_LSTATUS);
return 0;
}
staticint as21xxx_read_c22_lpa(struct phy_device *phydev)
{ int lpagb;
/* MII_STAT1000 are only filled in the mapped C22 * in C45, use that to fill lpagb values and check.
*/
lpagb = phy_read_mmd(phydev, MDIO_MMD_AN,
AS21XXX_MDIO_AN_C22 + MII_STAT1000); if (lpagb < 0) return lpagb;
if (lpagb & LPA_1000MSFAIL) { int adv = phy_read_mmd(phydev, MDIO_MMD_AN,
AS21XXX_MDIO_AN_C22 + MII_CTRL1000);
for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) if (rules == as21xxx_led_supported_pattern[i].pattern) return 0;
return -EOPNOTSUPP;
}
staticint as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index, unsignedlong *rules)
{ int i, val;
if (index > AEON_MAX_LEDS) return -EINVAL;
val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index)); if (val < 0) return val;
val = FIELD_GET(VEND1_LED_REG_A_EVENT, val); for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) if (val == as21xxx_led_supported_pattern[i].val) {
*rules = as21xxx_led_supported_pattern[i].pattern; return 0;
}
/* Should be impossible */ return -EINVAL;
}
staticint as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index, unsignedlong rules)
{
u16 val = 0; int i;
if (index > AEON_MAX_LEDS) return -EINVAL;
for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) if (rules == as21xxx_led_supported_pattern[i].pattern) {
val = as21xxx_led_supported_pattern[i].val; break;
}
/* Skip PHY that are not AS21xxx */ if (!phy_id_compare_vendor(phydev->c45_ids.device_ids[MDIO_MMD_PCS],
PHY_VENDOR_AEONSEMI)) return genphy_match_phy_device(phydev, phydrv);
/* Read PHY ID to handle firmware loaded or HW reset */
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); if (ret < 0) return ret;
phy_id = ret << 16;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2); if (ret < 0) return ret;
phy_id |= ret;
/* With PHY ID not the generic AS21xxx one assume * the firmware just loaded
*/ if (phy_id != PHY_ID_AS21XXX) return phy_id == phydrv->phy_id;
/* Allocate temp priv and load the firmware */
priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
mutex_init(&priv->ipc_lock);
ret = aeon_firmware_load(phydev); if (ret) goto out;
/* Sync parity... */
ret = aeon_ipc_sync_parity(phydev, priv); if (ret) goto out;
/* ...and send a third NOOP cmd to wait for firmware finish loading */
ret = aeon_ipc_noop(phydev, priv, &ret_sts); if (ret) goto out;
out:
mutex_destroy(&priv->ipc_lock);
kfree(priv);
/* Return can either be 0 or a negative error code. * Returning 0 here means THIS is NOT a suitable PHY. * * For the specific case of the generic Aeonsemi PHY ID that * needs the firmware the be loaded first to have a correct PHY ID, * this is OK as a matching PHY ID will be found right after. * This relies on the driver probe order where the first PHY driver * probed is the generic one.
*/ 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.