lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Tue, 31 Mar 2020 20:47:38 +0300 From: Baruch Siach <baruch@...s.co.il> To: Russell King <linux@...linux.org.uk> Cc: netdev@...r.kernel.org, Shmuel Hazan <sh@...s.co.il>, Andrew Lunn <andrew@...n.ch>, Florian Fainelli <f.fainelli@...il.com>, Heiner Kallweit <hkallweit1@...il.com>, Baruch Siach <baruch@...s.co.il> Subject: [PATCH] net: phy: marvell10g: add firmware load support When Marvell 88X3310 and 88E2110 hardware configuration SPI_CONFIG strap bit is pulled up, the host must load firmware to the PHY after reset. Add support for loading firmware. Firmware files are available from Marvell under NDA. Signed-off-by: Baruch Siach <baruch@...s.co.il> --- drivers/net/phy/marvell10g.c | 114 +++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 64c9f3bba2cd..9572426ba1c6 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -27,13 +27,28 @@ #include <linux/marvell_phy.h> #include <linux/phy.h> #include <linux/sfp.h> +#include <linux/firmware.h> +#include <linux/delay.h> #define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe #define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) +#define MV_FIRMWARE_HEADER_SIZE 32 + enum { MV_PMA_BOOT = 0xc050, MV_PMA_BOOT_FATAL = BIT(0), + MV_PMA_BOOT_PROGRESS_MASK = 0x0006, + MV_PMA_BOOT_WAITING = 0x0002, + MV_PMA_BOOT_FW_LOADED = BIT(6), + + MV_PCS_FW_LOW_WORD = 0xd0f0, + MV_PCS_FW_HIGH_WORD = 0xd0f1, + MV_PCS_RAM_DATA = 0xd0f2, + MV_PCS_RAM_CHECKSUM = 0xd0f3, + + MV_PMA_FW_REV1 = 0xc011, + MV_PMA_FW_REV2 = 0xc012, MV_PCS_BASE_T = 0x0000, MV_PCS_BASE_R = 0x1000, @@ -223,6 +238,99 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) return 0; } +static int mv3310_write_firmware(struct phy_device *phydev, const u8 *data, + unsigned int size) +{ + unsigned int low_byte, high_byte; + u16 checksum = 0, ram_checksum; + unsigned int i = 0; + + while (i < size) { + low_byte = data[i++]; + high_byte = data[i++]; + checksum += low_byte + high_byte; + phy_write_mmd(phydev, MDIO_MMD_PCS, MV_PCS_RAM_DATA, + (high_byte << 8) | low_byte); + cond_resched(); + } + + ram_checksum = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_RAM_CHECKSUM); + if (ram_checksum != checksum) { + dev_err(&phydev->mdio.dev, "firmware checksum failed"); + return -EIO; + } + + return 0; +} + +static void mv3310_report_firmware_rev(struct phy_device *phydev) +{ + int rev1, rev2; + + rev1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_REV1); + rev2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_REV2); + if (rev1 < 0 || rev2 < 0) + return; + + dev_info(&phydev->mdio.dev, "Loaded firmware revision %d.%d.%d.%d", + (rev1 & 0xff00) >> 8, rev1 & 0x00ff, + (rev2 & 0xff00) >> 8, rev2 & 0x00ff); +} + +static int mv3310_load_firmware(struct phy_device *phydev) +{ + const struct firmware *fw_entry; + char *fw_file; + int ret; + + switch (phydev->drv->phy_id) { + case MARVELL_PHY_ID_88X3310: + fw_file = "mrvl/x3310fw.hdr"; + break; + case MARVELL_PHY_ID_88E2110: + fw_file = "mrvl/e21x0fw.hdr"; + break; + default: + dev_warn(&phydev->mdio.dev, "unknown firmware file for %s PHY", + phydev->drv->name); + return -EINVAL; + } + + ret = request_firmware(&fw_entry, fw_file, &phydev->mdio.dev); + if (ret < 0) + return ret; + + /* Firmware size must be larger than header, and even */ + if (fw_entry->size <= MV_FIRMWARE_HEADER_SIZE || + (fw_entry->size % 2) != 0) { + dev_err(&phydev->mdio.dev, "firmware file invalid"); + return -EINVAL; + } + + /* Clear checksum register */ + phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_RAM_CHECKSUM); + + /* Set firmware load address */ + phy_write_mmd(phydev, MDIO_MMD_PCS, MV_PCS_FW_LOW_WORD, 0); + phy_write_mmd(phydev, MDIO_MMD_PCS, MV_PCS_FW_HIGH_WORD, 0x0010); + + ret = mv3310_write_firmware(phydev, + fw_entry->data + MV_FIRMWARE_HEADER_SIZE, + fw_entry->size - MV_FIRMWARE_HEADER_SIZE); + if (ret < 0) + return ret; + + phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT, + MV_PMA_BOOT_FW_LOADED, MV_PMA_BOOT_FW_LOADED); + + release_firmware(fw_entry); + + msleep(100); + mv3310_report_firmware_rev(phydev); + + return 0; +} + static const struct sfp_upstream_ops mv3310_sfp_ops = { .attach = phy_sfp_attach, .detach = phy_sfp_detach, @@ -249,6 +357,12 @@ static int mv3310_probe(struct phy_device *phydev) return -ENODEV; } + if ((ret & MV_PMA_BOOT_PROGRESS_MASK) == MV_PMA_BOOT_WAITING) { + ret = mv3310_load_firmware(phydev); + if (ret < 0) + return ret; + } + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; -- 2.25.1
Powered by blists - more mailing lists