From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5522EC43331 for ; Tue, 31 Mar 2020 17:55:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3776E212CC for ; Tue, 31 Mar 2020 17:55:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726282AbgCaRzl (ORCPT ); Tue, 31 Mar 2020 13:55:41 -0400 Received: from guitar.tcltek.co.il ([192.115.133.116]:52388 "EHLO mx.tkos.co.il" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725999AbgCaRzl (ORCPT ); Tue, 31 Mar 2020 13:55:41 -0400 X-Greylist: delayed 460 seconds by postgrey-1.27 at vger.kernel.org; Tue, 31 Mar 2020 13:55:40 EDT Received: from tarshish.tkos.co.il (unknown [10.0.8.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mx.tkos.co.il (Postfix) with ESMTPS id 4043A440610; Tue, 31 Mar 2020 20:47:38 +0300 (IDT) From: Baruch Siach To: Russell King Cc: netdev@vger.kernel.org, Shmuel Hazan , Andrew Lunn , Florian Fainelli , Heiner Kallweit , Baruch Siach Subject: [PATCH] net: phy: marvell10g: add firmware load support Date: Tue, 31 Mar 2020 20:47:38 +0300 Message-Id: <16e4a15e359012fc485d22c7e413a129029fbd0f.1585676858.git.baruch@tkos.co.il> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 #include #include +#include +#include #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