xref: /openbmc/u-boot/drivers/net/phy/aquantia.c (revision 68489ed037530ec29fc0bc452ad6e4b0c5db02ec)
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