183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2f7c38cf8SShaohui Xie /*
3f7c38cf8SShaohui Xie * Aquantia PHY drivers
4f7c38cf8SShaohui Xie *
5f7c38cf8SShaohui Xie * Copyright 2014 Freescale Semiconductor, Inc.
6c54bfbf9SValentin-catalin Neacsu * Copyright 2018 NXP
7f7c38cf8SShaohui Xie */
8f7c38cf8SShaohui Xie #include <config.h>
9f7c38cf8SShaohui Xie #include <common.h>
10365108efSCalvin Johnson #include <dm.h>
11f7c38cf8SShaohui Xie #include <phy.h>
12a740ee91SPhilipp Tomsich #include <u-boot/crc.h>
134506423aSJeremy Gebben #include <malloc.h>
144506423aSJeremy Gebben #include <asm/byteorder.h>
154506423aSJeremy Gebben #include <fs.h>
16f7c38cf8SShaohui Xie
17f7c38cf8SShaohui Xie #define AQUNTIA_10G_CTL 0x20
18f7c38cf8SShaohui Xie #define AQUNTIA_VENDOR_P1 0xc400
19f7c38cf8SShaohui Xie
20f7c38cf8SShaohui Xie #define AQUNTIA_SPEED_LSB_MASK 0x2000
21f7c38cf8SShaohui Xie #define AQUNTIA_SPEED_MSB_MASK 0x40
22f7c38cf8SShaohui Xie
23c54bfbf9SValentin-catalin Neacsu #define AQUANTIA_SYSTEM_INTERFACE_SR 0xe812
24c54bfbf9SValentin-catalin Neacsu #define AQUANTIA_VENDOR_PROVISIONING_REG 0xC441
25*91c9cbabSValentin-catalin Neacsu #define AQUANTIA_FIRMWARE_ID 0x20
26*91c9cbabSValentin-catalin Neacsu #define AQUANTIA_RESERVED_STATUS 0xc885
27*91c9cbabSValentin-catalin Neacsu #define AQUANTIA_FIRMWARE_MAJOR_MASK 0xff00
28*91c9cbabSValentin-catalin Neacsu #define AQUANTIA_FIRMWARE_MINOR_MASK 0xff
29*91c9cbabSValentin-catalin Neacsu #define AQUANTIA_FIRMWARE_BUILD_MASK 0xf0
30c54bfbf9SValentin-catalin Neacsu
31c54bfbf9SValentin-catalin Neacsu #define AQUANTIA_USX_AUTONEG_CONTROL_ENA 0x0008
32c54bfbf9SValentin-catalin Neacsu #define AQUANTIA_SI_IN_USE_MASK 0x0078
33c54bfbf9SValentin-catalin Neacsu #define AQUANTIA_SI_USXGMII 0x0018
34c54bfbf9SValentin-catalin Neacsu
354506423aSJeremy Gebben /* registers in MDIO_MMD_VEND1 region */
364506423aSJeremy Gebben #define GLOBAL_FIRMWARE_ID 0x20
374506423aSJeremy Gebben #define GLOBAL_FAULT 0xc850
384506423aSJeremy Gebben #define GLOBAL_RSTATUS_1 0xc885
394506423aSJeremy Gebben
404506423aSJeremy Gebben #define GLOBAL_STANDARD_CONTROL 0x0
414506423aSJeremy Gebben #define SOFT_RESET BIT(15)
424506423aSJeremy Gebben #define LOW_POWER BIT(11)
434506423aSJeremy Gebben
444506423aSJeremy Gebben #define MAILBOX_CONTROL 0x0200
454506423aSJeremy Gebben #define MAILBOX_EXECUTE BIT(15)
464506423aSJeremy Gebben #define MAILBOX_WRITE BIT(14)
474506423aSJeremy Gebben #define MAILBOX_RESET_CRC BIT(12)
484506423aSJeremy Gebben #define MAILBOX_BUSY BIT(8)
494506423aSJeremy Gebben
504506423aSJeremy Gebben #define MAILBOX_CRC 0x0201
514506423aSJeremy Gebben
524506423aSJeremy Gebben #define MAILBOX_ADDR_MSW 0x0202
534506423aSJeremy Gebben #define MAILBOX_ADDR_LSW 0x0203
544506423aSJeremy Gebben
554506423aSJeremy Gebben #define MAILBOX_DATA_MSW 0x0204
564506423aSJeremy Gebben #define MAILBOX_DATA_LSW 0x0205
574506423aSJeremy Gebben
584506423aSJeremy Gebben #define UP_CONTROL 0xc001
594506423aSJeremy Gebben #define UP_RESET BIT(15)
604506423aSJeremy Gebben #define UP_RUN_STALL_OVERRIDE BIT(6)
614506423aSJeremy Gebben #define UP_RUN_STALL BIT(0)
624506423aSJeremy Gebben
634506423aSJeremy Gebben /* addresses of memory segments in the phy */
644506423aSJeremy Gebben #define DRAM_BASE_ADDR 0x3FFE0000
654506423aSJeremy Gebben #define IRAM_BASE_ADDR 0x40000000
664506423aSJeremy Gebben
674506423aSJeremy Gebben /* firmware image format constants */
684506423aSJeremy Gebben #define VERSION_STRING_SIZE 0x40
694506423aSJeremy Gebben #define VERSION_STRING_OFFSET 0x0200
704506423aSJeremy Gebben #define HEADER_OFFSET 0x300
714506423aSJeremy Gebben
724506423aSJeremy Gebben #pragma pack(1)
734506423aSJeremy Gebben struct fw_header {
744506423aSJeremy Gebben u8 padding[4];
754506423aSJeremy Gebben u8 iram_offset[3];
764506423aSJeremy Gebben u8 iram_size[3];
774506423aSJeremy Gebben u8 dram_offset[3];
784506423aSJeremy Gebben u8 dram_size[3];
794506423aSJeremy Gebben };
804506423aSJeremy Gebben
814506423aSJeremy Gebben #pragma pack()
824506423aSJeremy Gebben
834506423aSJeremy Gebben #if defined(CONFIG_PHY_AQUANTIA_UPLOAD_FW)
aquantia_read_fw(u8 ** fw_addr,size_t * fw_length)844506423aSJeremy Gebben static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
854506423aSJeremy Gebben {
864506423aSJeremy Gebben loff_t length, read;
874506423aSJeremy Gebben int ret;
884506423aSJeremy Gebben void *addr = NULL;
894506423aSJeremy Gebben
904506423aSJeremy Gebben *fw_addr = NULL;
914506423aSJeremy Gebben *fw_length = 0;
924506423aSJeremy Gebben debug("Loading Acquantia microcode from %s %s\n",
934506423aSJeremy Gebben CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME);
944506423aSJeremy Gebben ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
954506423aSJeremy Gebben if (ret < 0)
964506423aSJeremy Gebben goto cleanup;
974506423aSJeremy Gebben
984506423aSJeremy Gebben ret = fs_size(CONFIG_PHY_AQUANTIA_FW_NAME, &length);
994506423aSJeremy Gebben if (ret < 0)
1004506423aSJeremy Gebben goto cleanup;
1014506423aSJeremy Gebben
1024506423aSJeremy Gebben addr = malloc(length);
1034506423aSJeremy Gebben if (!addr) {
1044506423aSJeremy Gebben ret = -ENOMEM;
1054506423aSJeremy Gebben goto cleanup;
1064506423aSJeremy Gebben }
1074506423aSJeremy Gebben
1084506423aSJeremy Gebben ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
1094506423aSJeremy Gebben if (ret < 0)
1104506423aSJeremy Gebben goto cleanup;
1114506423aSJeremy Gebben
1124506423aSJeremy Gebben ret = fs_read(CONFIG_PHY_AQUANTIA_FW_NAME, (ulong)addr, 0, length,
1134506423aSJeremy Gebben &read);
1144506423aSJeremy Gebben if (ret < 0)
1154506423aSJeremy Gebben goto cleanup;
1164506423aSJeremy Gebben
1174506423aSJeremy Gebben *fw_addr = addr;
1184506423aSJeremy Gebben *fw_length = length;
1194506423aSJeremy Gebben debug("Found Acquantia microcode.\n");
1204506423aSJeremy Gebben
1214506423aSJeremy Gebben cleanup:
1224506423aSJeremy Gebben if (ret < 0) {
1234506423aSJeremy Gebben printf("loading firmware file %s %s failed with error %d\n",
1244506423aSJeremy Gebben CONFIG_PHY_AQUANTIA_FW_PART,
1254506423aSJeremy Gebben CONFIG_PHY_AQUANTIA_FW_NAME, ret);
1264506423aSJeremy Gebben free(addr);
1274506423aSJeremy Gebben }
1284506423aSJeremy Gebben return ret;
1294506423aSJeremy Gebben }
1304506423aSJeremy Gebben
1314506423aSJeremy Gebben /* load data into the phy's memory */
aquantia_load_memory(struct phy_device * phydev,u32 addr,const u8 * data,size_t len)1324506423aSJeremy Gebben static int aquantia_load_memory(struct phy_device *phydev, u32 addr,
1334506423aSJeremy Gebben const u8 *data, size_t len)
1344506423aSJeremy Gebben {
1354506423aSJeremy Gebben size_t pos;
1364506423aSJeremy Gebben u16 crc = 0, up_crc;
1374506423aSJeremy Gebben
1384506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_CONTROL, MAILBOX_RESET_CRC);
1394506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_ADDR_MSW, addr >> 16);
1404506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_ADDR_LSW, addr & 0xfffc);
1414506423aSJeremy Gebben
1424506423aSJeremy Gebben for (pos = 0; pos < len; pos += min(sizeof(u32), len - pos)) {
1434506423aSJeremy Gebben u32 word = 0;
1444506423aSJeremy Gebben
1454506423aSJeremy Gebben memcpy(&word, &data[pos], min(sizeof(u32), len - pos));
1464506423aSJeremy Gebben
1474506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_DATA_MSW,
1484506423aSJeremy Gebben (word >> 16));
1494506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_DATA_LSW,
1504506423aSJeremy Gebben word & 0xffff);
1514506423aSJeremy Gebben
1524506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_CONTROL,
1534506423aSJeremy Gebben MAILBOX_EXECUTE | MAILBOX_WRITE);
1544506423aSJeremy Gebben
1554506423aSJeremy Gebben /* keep a big endian CRC to match the phy processor */
1564506423aSJeremy Gebben word = cpu_to_be32(word);
1574506423aSJeremy Gebben crc = crc16_ccitt(crc, (u8 *)&word, sizeof(word));
1584506423aSJeremy Gebben }
1594506423aSJeremy Gebben
1604506423aSJeremy Gebben up_crc = phy_read(phydev, MDIO_MMD_VEND1, MAILBOX_CRC);
1614506423aSJeremy Gebben if (crc != up_crc) {
1624506423aSJeremy Gebben printf("%s crc mismatch: calculated 0x%04hx phy 0x%04hx\n",
1634506423aSJeremy Gebben phydev->dev->name, crc, up_crc);
1644506423aSJeremy Gebben return -EINVAL;
1654506423aSJeremy Gebben }
1664506423aSJeremy Gebben return 0;
1674506423aSJeremy Gebben }
1684506423aSJeremy Gebben
unpack_u24(const u8 * data)1694506423aSJeremy Gebben static u32 unpack_u24(const u8 *data)
1704506423aSJeremy Gebben {
1714506423aSJeremy Gebben return (data[2] << 16) + (data[1] << 8) + data[0];
1724506423aSJeremy Gebben }
1734506423aSJeremy Gebben
aquantia_upload_firmware(struct phy_device * phydev)1744506423aSJeremy Gebben static int aquantia_upload_firmware(struct phy_device *phydev)
1754506423aSJeremy Gebben {
1764506423aSJeremy Gebben int ret;
1774506423aSJeremy Gebben u8 *addr = NULL;
1784506423aSJeremy Gebben size_t fw_length = 0;
1794506423aSJeremy Gebben u16 calculated_crc, read_crc;
1804506423aSJeremy Gebben char version[VERSION_STRING_SIZE];
1814506423aSJeremy Gebben u32 primary_offset, iram_offset, iram_size, dram_offset, dram_size;
1824506423aSJeremy Gebben const struct fw_header *header;
1834506423aSJeremy Gebben
1844506423aSJeremy Gebben ret = aquantia_read_fw(&addr, &fw_length);
1854506423aSJeremy Gebben if (ret != 0)
1864506423aSJeremy Gebben return ret;
1874506423aSJeremy Gebben
1884506423aSJeremy Gebben read_crc = (addr[fw_length - 2] << 8) | addr[fw_length - 1];
1894506423aSJeremy Gebben calculated_crc = crc16_ccitt(0, addr, fw_length - 2);
1904506423aSJeremy Gebben if (read_crc != calculated_crc) {
1914506423aSJeremy Gebben printf("%s bad firmware crc: file 0x%04x calculated 0x%04x\n",
1924506423aSJeremy Gebben phydev->dev->name, read_crc, calculated_crc);
1934506423aSJeremy Gebben ret = -EINVAL;
1944506423aSJeremy Gebben goto done;
1954506423aSJeremy Gebben }
1964506423aSJeremy Gebben
1974506423aSJeremy Gebben /* Find the DRAM and IRAM sections within the firmware file. */
1984506423aSJeremy Gebben primary_offset = ((addr[9] & 0xf) << 8 | addr[8]) << 12;
1994506423aSJeremy Gebben
2004506423aSJeremy Gebben header = (struct fw_header *)&addr[primary_offset + HEADER_OFFSET];
2014506423aSJeremy Gebben
2024506423aSJeremy Gebben iram_offset = primary_offset + unpack_u24(header->iram_offset);
2034506423aSJeremy Gebben iram_size = unpack_u24(header->iram_size);
2044506423aSJeremy Gebben
2054506423aSJeremy Gebben dram_offset = primary_offset + unpack_u24(header->dram_offset);
2064506423aSJeremy Gebben dram_size = unpack_u24(header->dram_size);
2074506423aSJeremy Gebben
2084506423aSJeremy Gebben debug("primary %d iram offset=%d size=%d dram offset=%d size=%d\n",
2094506423aSJeremy Gebben primary_offset, iram_offset, iram_size, dram_offset, dram_size);
2104506423aSJeremy Gebben
2114506423aSJeremy Gebben strlcpy(version, (char *)&addr[dram_offset + VERSION_STRING_OFFSET],
2124506423aSJeremy Gebben VERSION_STRING_SIZE);
2134506423aSJeremy Gebben printf("%s loading firmare version '%s'\n", phydev->dev->name, version);
2144506423aSJeremy Gebben
2154506423aSJeremy Gebben /* stall the microcprocessor */
2164506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
2174506423aSJeremy Gebben UP_RUN_STALL | UP_RUN_STALL_OVERRIDE);
2184506423aSJeremy Gebben
2194506423aSJeremy Gebben debug("loading dram 0x%08x from offset=%d size=%d\n",
2204506423aSJeremy Gebben DRAM_BASE_ADDR, dram_offset, dram_size);
2214506423aSJeremy Gebben ret = aquantia_load_memory(phydev, DRAM_BASE_ADDR, &addr[dram_offset],
2224506423aSJeremy Gebben dram_size);
2234506423aSJeremy Gebben if (ret != 0)
2244506423aSJeremy Gebben goto done;
2254506423aSJeremy Gebben
2264506423aSJeremy Gebben debug("loading iram 0x%08x from offset=%d size=%d\n",
2274506423aSJeremy Gebben IRAM_BASE_ADDR, iram_offset, iram_size);
2284506423aSJeremy Gebben ret = aquantia_load_memory(phydev, IRAM_BASE_ADDR, &addr[iram_offset],
2294506423aSJeremy Gebben iram_size);
2304506423aSJeremy Gebben if (ret != 0)
2314506423aSJeremy Gebben goto done;
2324506423aSJeremy Gebben
2334506423aSJeremy Gebben /* make sure soft reset and low power mode are clear */
2344506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, GLOBAL_STANDARD_CONTROL, 0);
2354506423aSJeremy Gebben
2364506423aSJeremy Gebben /* Release the microprocessor. UP_RESET must be held for 100 usec. */
2374506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
2384506423aSJeremy Gebben UP_RUN_STALL | UP_RUN_STALL_OVERRIDE | UP_RESET);
2394506423aSJeremy Gebben
2404506423aSJeremy Gebben udelay(100);
2414506423aSJeremy Gebben
2424506423aSJeremy Gebben phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, UP_RUN_STALL_OVERRIDE);
2434506423aSJeremy Gebben
2444506423aSJeremy Gebben printf("%s firmare loading done.\n", phydev->dev->name);
2454506423aSJeremy Gebben done:
2464506423aSJeremy Gebben free(addr);
2474506423aSJeremy Gebben return ret;
2484506423aSJeremy Gebben }
2494506423aSJeremy Gebben #else
aquantia_upload_firmware(struct phy_device * phydev)2504506423aSJeremy Gebben static int aquantia_upload_firmware(struct phy_device *phydev)
2514506423aSJeremy Gebben {
25230a2c8ccSJeremy Gebben printf("ERROR %s firmware loading disabled.\n", phydev->dev->name);
25330a2c8ccSJeremy Gebben return -1;
2544506423aSJeremy Gebben }
2554506423aSJeremy Gebben #endif
2564506423aSJeremy Gebben
aquantia_config(struct phy_device * phydev)257f7c38cf8SShaohui Xie int aquantia_config(struct phy_device *phydev)
258f7c38cf8SShaohui Xie {
25930a2c8ccSJeremy Gebben u32 val, id, rstatus, fault;
260*91c9cbabSValentin-catalin Neacsu u32 reg_val1 = 0;
26130a2c8ccSJeremy Gebben
26230a2c8ccSJeremy Gebben id = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FIRMWARE_ID);
26330a2c8ccSJeremy Gebben rstatus = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_RSTATUS_1);
26430a2c8ccSJeremy Gebben fault = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FAULT);
26530a2c8ccSJeremy Gebben
26630a2c8ccSJeremy Gebben if (id != 0)
26730a2c8ccSJeremy Gebben printf("%s running firmware version %X.%X.%X\n",
26830a2c8ccSJeremy Gebben phydev->dev->name, (id >> 8), id & 0xff,
26930a2c8ccSJeremy Gebben (rstatus >> 4) & 0xf);
27030a2c8ccSJeremy Gebben
27130a2c8ccSJeremy Gebben if (fault != 0)
27230a2c8ccSJeremy Gebben printf("%s fault 0x%04x detected\n", phydev->dev->name, fault);
27330a2c8ccSJeremy Gebben
27430a2c8ccSJeremy Gebben if (id == 0 || fault != 0) {
2754506423aSJeremy Gebben int ret;
2764506423aSJeremy Gebben
2774506423aSJeremy Gebben ret = aquantia_upload_firmware(phydev);
2784506423aSJeremy Gebben if (ret != 0)
2794506423aSJeremy Gebben return ret;
28030a2c8ccSJeremy Gebben }
2814506423aSJeremy Gebben
2824506423aSJeremy Gebben val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR);
283f7c38cf8SShaohui Xie
284f7c38cf8SShaohui Xie if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
285f7c38cf8SShaohui Xie /* 1000BASE-T mode */
286f7c38cf8SShaohui Xie phydev->advertising = SUPPORTED_1000baseT_Full;
287f7c38cf8SShaohui Xie phydev->supported = phydev->advertising;
288f7c38cf8SShaohui Xie
289f7c38cf8SShaohui Xie val = (val & ~AQUNTIA_SPEED_LSB_MASK) | AQUNTIA_SPEED_MSB_MASK;
290f7c38cf8SShaohui Xie phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val);
291f7c38cf8SShaohui Xie } else if (phydev->interface == PHY_INTERFACE_MODE_XGMII) {
292f7c38cf8SShaohui Xie /* 10GBASE-T mode */
293f7c38cf8SShaohui Xie phydev->advertising = SUPPORTED_10000baseT_Full;
294f7c38cf8SShaohui Xie phydev->supported = phydev->advertising;
295f7c38cf8SShaohui Xie
296f7c38cf8SShaohui Xie if (!(val & AQUNTIA_SPEED_LSB_MASK) ||
297f7c38cf8SShaohui Xie !(val & AQUNTIA_SPEED_MSB_MASK))
298f7c38cf8SShaohui Xie phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR,
299f7c38cf8SShaohui Xie AQUNTIA_SPEED_LSB_MASK |
300f7c38cf8SShaohui Xie AQUNTIA_SPEED_MSB_MASK);
301c54bfbf9SValentin-catalin Neacsu
302c54bfbf9SValentin-catalin Neacsu val = phy_read(phydev, MDIO_MMD_PHYXS,
303c54bfbf9SValentin-catalin Neacsu AQUANTIA_SYSTEM_INTERFACE_SR);
304c54bfbf9SValentin-catalin Neacsu /* If SI is USXGMII then start USXGMII autoneg */
305c54bfbf9SValentin-catalin Neacsu if ((val & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII) {
306c54bfbf9SValentin-catalin Neacsu phy_write(phydev, MDIO_MMD_PHYXS,
307c54bfbf9SValentin-catalin Neacsu AQUANTIA_VENDOR_PROVISIONING_REG,
308c54bfbf9SValentin-catalin Neacsu AQUANTIA_USX_AUTONEG_CONTROL_ENA);
309*91c9cbabSValentin-catalin Neacsu printf("%s: system interface USXGMII\n",
310*91c9cbabSValentin-catalin Neacsu phydev->dev->name);
311*91c9cbabSValentin-catalin Neacsu } else {
312*91c9cbabSValentin-catalin Neacsu printf("%s: system interface XFI\n",
313*91c9cbabSValentin-catalin Neacsu phydev->dev->name);
314c54bfbf9SValentin-catalin Neacsu }
315c54bfbf9SValentin-catalin Neacsu
316f7c38cf8SShaohui Xie } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII_2500) {
317f7c38cf8SShaohui Xie /* 2.5GBASE-T mode */
318f7c38cf8SShaohui Xie phydev->advertising = SUPPORTED_1000baseT_Full;
319f7c38cf8SShaohui Xie phydev->supported = phydev->advertising;
320f7c38cf8SShaohui Xie
321f7c38cf8SShaohui Xie phy_write(phydev, MDIO_MMD_AN, AQUNTIA_10G_CTL, 1);
322f7c38cf8SShaohui Xie phy_write(phydev, MDIO_MMD_AN, AQUNTIA_VENDOR_P1, 0x9440);
323f7c38cf8SShaohui Xie } else if (phydev->interface == PHY_INTERFACE_MODE_MII) {
324f7c38cf8SShaohui Xie /* 100BASE-TX mode */
325f7c38cf8SShaohui Xie phydev->advertising = SUPPORTED_100baseT_Full;
326f7c38cf8SShaohui Xie phydev->supported = phydev->advertising;
327f7c38cf8SShaohui Xie
328f7c38cf8SShaohui Xie val = (val & ~AQUNTIA_SPEED_MSB_MASK) | AQUNTIA_SPEED_LSB_MASK;
329f7c38cf8SShaohui Xie phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val);
330f7c38cf8SShaohui Xie }
331*91c9cbabSValentin-catalin Neacsu
332*91c9cbabSValentin-catalin Neacsu val = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_RESERVED_STATUS);
333*91c9cbabSValentin-catalin Neacsu reg_val1 = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_FIRMWARE_ID);
334*91c9cbabSValentin-catalin Neacsu
335*91c9cbabSValentin-catalin Neacsu printf("%s: %s Firmware Version %x.%x.%x\n", phydev->dev->name,
336*91c9cbabSValentin-catalin Neacsu phydev->drv->name,
337*91c9cbabSValentin-catalin Neacsu (reg_val1 & AQUANTIA_FIRMWARE_MAJOR_MASK) >> 8,
338*91c9cbabSValentin-catalin Neacsu reg_val1 & AQUANTIA_FIRMWARE_MINOR_MASK,
339*91c9cbabSValentin-catalin Neacsu (val & AQUANTIA_FIRMWARE_BUILD_MASK) >> 4);
340*91c9cbabSValentin-catalin Neacsu
341f7c38cf8SShaohui Xie return 0;
342f7c38cf8SShaohui Xie }
343f7c38cf8SShaohui Xie
aquantia_startup(struct phy_device * phydev)344f7c38cf8SShaohui Xie int aquantia_startup(struct phy_device *phydev)
345f7c38cf8SShaohui Xie {
346f7c38cf8SShaohui Xie u32 reg, speed;
347f7c38cf8SShaohui Xie int i = 0;
348f7c38cf8SShaohui Xie
349f7c38cf8SShaohui Xie phydev->duplex = DUPLEX_FULL;
350f7c38cf8SShaohui Xie
351f7c38cf8SShaohui Xie /* if the AN is still in progress, wait till timeout. */
352f7c38cf8SShaohui Xie phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
353f7c38cf8SShaohui Xie reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
354f7c38cf8SShaohui Xie if (!(reg & MDIO_AN_STAT1_COMPLETE)) {
355f7c38cf8SShaohui Xie printf("%s Waiting for PHY auto negotiation to complete",
356f7c38cf8SShaohui Xie phydev->dev->name);
357f7c38cf8SShaohui Xie do {
358f7c38cf8SShaohui Xie udelay(1000);
359f7c38cf8SShaohui Xie reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
360f7c38cf8SShaohui Xie if ((i++ % 500) == 0)
361f7c38cf8SShaohui Xie printf(".");
362f7c38cf8SShaohui Xie } while (!(reg & MDIO_AN_STAT1_COMPLETE) &&
363f7c38cf8SShaohui Xie i < (4 * PHY_ANEG_TIMEOUT));
364f7c38cf8SShaohui Xie
365f7c38cf8SShaohui Xie if (i > PHY_ANEG_TIMEOUT)
366f7c38cf8SShaohui Xie printf(" TIMEOUT !\n");
367f7c38cf8SShaohui Xie }
368f7c38cf8SShaohui Xie
369f7c38cf8SShaohui Xie /* Read twice because link state is latched and a
370f7c38cf8SShaohui Xie * read moves the current state into the register */
371f7c38cf8SShaohui Xie phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
372f7c38cf8SShaohui Xie reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
373f7c38cf8SShaohui Xie if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
374f7c38cf8SShaohui Xie phydev->link = 0;
375f7c38cf8SShaohui Xie else
376f7c38cf8SShaohui Xie phydev->link = 1;
377f7c38cf8SShaohui Xie
378f7c38cf8SShaohui Xie speed = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR);
379f7c38cf8SShaohui Xie if (speed & AQUNTIA_SPEED_MSB_MASK) {
380f7c38cf8SShaohui Xie if (speed & AQUNTIA_SPEED_LSB_MASK)
381f7c38cf8SShaohui Xie phydev->speed = SPEED_10000;
382f7c38cf8SShaohui Xie else
383f7c38cf8SShaohui Xie phydev->speed = SPEED_1000;
384f7c38cf8SShaohui Xie } else {
385f7c38cf8SShaohui Xie if (speed & AQUNTIA_SPEED_LSB_MASK)
386f7c38cf8SShaohui Xie phydev->speed = SPEED_100;
387f7c38cf8SShaohui Xie else
388f7c38cf8SShaohui Xie phydev->speed = SPEED_10;
389f7c38cf8SShaohui Xie }
390f7c38cf8SShaohui Xie
391f7c38cf8SShaohui Xie return 0;
392f7c38cf8SShaohui Xie }
393f7c38cf8SShaohui Xie
394f7c38cf8SShaohui Xie struct phy_driver aq1202_driver = {
395f7c38cf8SShaohui Xie .name = "Aquantia AQ1202",
396f7c38cf8SShaohui Xie .uid = 0x3a1b445,
397f7c38cf8SShaohui Xie .mask = 0xfffffff0,
398f7c38cf8SShaohui Xie .features = PHY_10G_FEATURES,
399f7c38cf8SShaohui Xie .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
400f7c38cf8SShaohui Xie MDIO_MMD_PHYXS | MDIO_MMD_AN |
401f7c38cf8SShaohui Xie MDIO_MMD_VEND1),
402f7c38cf8SShaohui Xie .config = &aquantia_config,
403f7c38cf8SShaohui Xie .startup = &aquantia_startup,
404f7c38cf8SShaohui Xie .shutdown = &gen10g_shutdown,
405f7c38cf8SShaohui Xie };
406f7c38cf8SShaohui Xie
407f7c38cf8SShaohui Xie struct phy_driver aq2104_driver = {
408f7c38cf8SShaohui Xie .name = "Aquantia AQ2104",
409f7c38cf8SShaohui Xie .uid = 0x3a1b460,
410f7c38cf8SShaohui Xie .mask = 0xfffffff0,
411f7c38cf8SShaohui Xie .features = PHY_10G_FEATURES,
412f7c38cf8SShaohui Xie .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
413f7c38cf8SShaohui Xie MDIO_MMD_PHYXS | MDIO_MMD_AN |
414f7c38cf8SShaohui Xie MDIO_MMD_VEND1),
415f7c38cf8SShaohui Xie .config = &aquantia_config,
416f7c38cf8SShaohui Xie .startup = &aquantia_startup,
417f7c38cf8SShaohui Xie .shutdown = &gen10g_shutdown,
418f7c38cf8SShaohui Xie };
419f7c38cf8SShaohui Xie
420f7c38cf8SShaohui Xie struct phy_driver aqr105_driver = {
421f7c38cf8SShaohui Xie .name = "Aquantia AQR105",
422f7c38cf8SShaohui Xie .uid = 0x3a1b4a2,
423f7c38cf8SShaohui Xie .mask = 0xfffffff0,
424f7c38cf8SShaohui Xie .features = PHY_10G_FEATURES,
425f7c38cf8SShaohui Xie .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
426f7c38cf8SShaohui Xie MDIO_MMD_PHYXS | MDIO_MMD_AN |
427f7c38cf8SShaohui Xie MDIO_MMD_VEND1),
428f7c38cf8SShaohui Xie .config = &aquantia_config,
429f7c38cf8SShaohui Xie .startup = &aquantia_startup,
430f7c38cf8SShaohui Xie .shutdown = &gen10g_shutdown,
431f7c38cf8SShaohui Xie };
432f8642ba6SShaohui Xie
43319c9ddaaSMingkai Hu struct phy_driver aqr106_driver = {
43419c9ddaaSMingkai Hu .name = "Aquantia AQR106",
43519c9ddaaSMingkai Hu .uid = 0x3a1b4d0,
43619c9ddaaSMingkai Hu .mask = 0xfffffff0,
43719c9ddaaSMingkai Hu .features = PHY_10G_FEATURES,
43819c9ddaaSMingkai Hu .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
43919c9ddaaSMingkai Hu MDIO_MMD_PHYXS | MDIO_MMD_AN |
44019c9ddaaSMingkai Hu MDIO_MMD_VEND1),
44119c9ddaaSMingkai Hu .config = &aquantia_config,
44219c9ddaaSMingkai Hu .startup = &aquantia_startup,
44319c9ddaaSMingkai Hu .shutdown = &gen10g_shutdown,
44419c9ddaaSMingkai Hu };
44519c9ddaaSMingkai Hu
44619c9ddaaSMingkai Hu struct phy_driver aqr107_driver = {
44719c9ddaaSMingkai Hu .name = "Aquantia AQR107",
44819c9ddaaSMingkai Hu .uid = 0x3a1b4e0,
44919c9ddaaSMingkai Hu .mask = 0xfffffff0,
45019c9ddaaSMingkai Hu .features = PHY_10G_FEATURES,
45119c9ddaaSMingkai Hu .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
45219c9ddaaSMingkai Hu MDIO_MMD_PHYXS | MDIO_MMD_AN |
45319c9ddaaSMingkai Hu MDIO_MMD_VEND1),
45419c9ddaaSMingkai Hu .config = &aquantia_config,
45519c9ddaaSMingkai Hu .startup = &aquantia_startup,
45619c9ddaaSMingkai Hu .shutdown = &gen10g_shutdown,
45719c9ddaaSMingkai Hu };
45819c9ddaaSMingkai Hu
459f8642ba6SShaohui Xie struct phy_driver aqr405_driver = {
460f8642ba6SShaohui Xie .name = "Aquantia AQR405",
461f8642ba6SShaohui Xie .uid = 0x3a1b4b2,
462f8642ba6SShaohui Xie .mask = 0xfffffff0,
463f8642ba6SShaohui Xie .features = PHY_10G_FEATURES,
464f8642ba6SShaohui Xie .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS|
465f8642ba6SShaohui Xie MDIO_MMD_PHYXS | MDIO_MMD_AN |
466f8642ba6SShaohui Xie MDIO_MMD_VEND1),
467f8642ba6SShaohui Xie .config = &aquantia_config,
468f8642ba6SShaohui Xie .startup = &aquantia_startup,
469f8642ba6SShaohui Xie .shutdown = &gen10g_shutdown,
470f8642ba6SShaohui Xie };
471f8642ba6SShaohui Xie
phy_aquantia_init(void)472f7c38cf8SShaohui Xie int phy_aquantia_init(void)
473f7c38cf8SShaohui Xie {
474f7c38cf8SShaohui Xie phy_register(&aq1202_driver);
475f7c38cf8SShaohui Xie phy_register(&aq2104_driver);
476f7c38cf8SShaohui Xie phy_register(&aqr105_driver);
47719c9ddaaSMingkai Hu phy_register(&aqr106_driver);
47819c9ddaaSMingkai Hu phy_register(&aqr107_driver);
479f8642ba6SShaohui Xie phy_register(&aqr405_driver);
480f7c38cf8SShaohui Xie
481f7c38cf8SShaohui Xie return 0;
482f7c38cf8SShaohui Xie }
483