diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Kconfig | 2 | ||||
-rw-r--r-- | drivers/net/fec_mxc.c | 121 | ||||
-rw-r--r-- | drivers/net/fec_mxc.h | 7 | ||||
-rw-r--r-- | drivers/net/mvpp2.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/Kconfig | 26 | ||||
-rw-r--r-- | drivers/net/phy/aquantia.c | 254 |
6 files changed, 389 insertions, 25 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f1f0e2d94e..39687431fb 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -176,7 +176,7 @@ config FEC_MXC_MDIO_BASE config FEC_MXC bool "FEC Ethernet controller" - depends on MX5 || MX6 || MX7 + depends on MX5 || MX6 || MX7 || IMX8 help This driver supports the 10/100 Fast Ethernet controller for NXP i.MX processors. diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index dac07b6e34..99c5c649a0 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -15,6 +15,7 @@ #include <miiphy.h> #include <net.h> #include <netdev.h> +#include <power/regulator.h> #include <asm/io.h> #include <linux/errno.h> @@ -122,6 +123,32 @@ static int fec_mdio_read(struct ethernet_regs *eth, uint8_t phyaddr, return val; } +static int fec_get_clk_rate(void *udev, int idx) +{ +#if IS_ENABLED(CONFIG_IMX8) + struct fec_priv *fec; + struct udevice *dev; + int ret; + + dev = udev; + if (!dev) { + ret = uclass_get_device(UCLASS_ETH, idx, &dev); + if (ret < 0) { + debug("Can't get FEC udev: %d\n", ret); + return ret; + } + } + + fec = dev_get_priv(dev); + if (fec) + return fec->clk_rate; + + return -EINVAL; +#else + return imx_get_fecclk(); +#endif +} + static void fec_mii_setspeed(struct ethernet_regs *eth) { /* @@ -139,9 +166,20 @@ static void fec_mii_setspeed(struct ethernet_regs *eth) * Given that ceil(clkrate / 5000000) <= 64, the calculation for * holdtime cannot result in a value greater than 3. */ - u32 pclk = imx_get_fecclk(); - u32 speed = DIV_ROUND_UP(pclk, 5000000); - u32 hold = DIV_ROUND_UP(pclk, 100000000) - 1; + u32 pclk; + u32 speed; + u32 hold; + int ret; + + ret = fec_get_clk_rate(NULL, 0); + if (ret < 0) { + printf("Can't find FEC0 clk rate: %d\n", ret); + return; + } + pclk = ret; + speed = DIV_ROUND_UP(pclk, 5000000); + hold = DIV_ROUND_UP(pclk, 100000000) - 1; + #ifdef FEC_QUIRK_ENET_MAC speed--; #endif @@ -1254,7 +1292,7 @@ static void fec_gpio_reset(struct fec_priv *priv) debug("fec_gpio_reset: fec_gpio_reset(dev)\n"); if (dm_gpio_is_valid(&priv->phy_reset_gpio)) { dm_gpio_set_value(&priv->phy_reset_gpio, 1); - udelay(priv->reset_delay); + mdelay(priv->reset_delay); dm_gpio_set_value(&priv->phy_reset_gpio, 0); } } @@ -1268,10 +1306,35 @@ static int fecmxc_probe(struct udevice *dev) uint32_t start; int ret; + if (IS_ENABLED(CONFIG_IMX8)) { + ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk); + if (ret < 0) { + debug("Can't get FEC ipg clk: %d\n", ret); + return ret; + } + ret = clk_enable(&priv->ipg_clk); + if (ret < 0) { + debug("Can't enable FEC ipg clk: %d\n", ret); + return ret; + } + + priv->clk_rate = clk_get_rate(&priv->ipg_clk); + } + ret = fec_alloc_descs(priv); if (ret) return ret; +#ifdef CONFIG_DM_REGULATOR + if (priv->phy_supply) { + ret = regulator_autoset(priv->phy_supply); + if (ret) { + printf("%s: Error enabling phy supply\n", dev->name); + return ret; + } + } +#endif + #ifdef CONFIG_DM_GPIO fec_gpio_reset(priv); #endif @@ -1301,8 +1364,27 @@ static int fecmxc_probe(struct udevice *dev) } priv->bus = bus; - priv->xcv_type = CONFIG_FEC_XCV_TYPE; priv->interface = pdata->phy_interface; + switch (priv->interface) { + case PHY_INTERFACE_MODE_MII: + priv->xcv_type = MII100; + break; + case PHY_INTERFACE_MODE_RMII: + priv->xcv_type = RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + priv->xcv_type = RGMII; + break; + default: + priv->xcv_type = CONFIG_FEC_XCV_TYPE; + printf("Unsupported interface type %d defaulting to %d\n", + priv->interface, priv->xcv_type); + break; + } + ret = fec_phy_init(priv, dev); if (ret) goto err_phy; @@ -1327,6 +1409,11 @@ static int fecmxc_remove(struct udevice *dev) mdio_unregister(priv->bus); mdio_free(priv->bus); +#ifdef CONFIG_DM_REGULATOR + if (priv->phy_supply) + regulator_set_enable(priv->phy_supply, false); +#endif + return 0; } @@ -1350,24 +1437,25 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev) return -EINVAL; } +#ifdef CONFIG_DM_REGULATOR + device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply); +#endif + #ifdef CONFIG_DM_GPIO ret = gpio_request_by_name(dev, "phy-reset-gpios", 0, - &priv->phy_reset_gpio, GPIOD_IS_OUT); - if (ret == 0) { - ret = dev_read_u32_array(dev, "phy-reset-duration", - &priv->reset_delay, 1); - } else if (ret == -ENOENT) { - priv->reset_delay = 1000; - ret = 0; - } + &priv->phy_reset_gpio, GPIOD_IS_OUT); + if (ret < 0) + return 0; /* property is optional, don't return error! */ + priv->reset_delay = dev_read_u32_default(dev, "phy-reset-duration", 1); if (priv->reset_delay > 1000) { - printf("FEX MXC: gpio reset timeout should be less the 1000\n"); - priv->reset_delay = 1000; + printf("FEC MXC: phy reset duration should be <= 1000ms\n"); + /* property value wrong, use default value */ + priv->reset_delay = 1; } #endif - return ret; + return 0; } static const struct udevice_id fecmxc_ids[] = { @@ -1376,6 +1464,7 @@ static const struct udevice_id fecmxc_ids[] = { { .compatible = "fsl,imx6sx-fec" }, { .compatible = "fsl,imx6ul-fec" }, { .compatible = "fsl,imx53-fec" }, + { .compatible = "fsl,imx7d-fec" }, { } }; diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h index fd89443205..e9a661f0a1 100644 --- a/drivers/net/fec_mxc.h +++ b/drivers/net/fec_mxc.h @@ -16,6 +16,8 @@ #ifndef __FEC_MXC_H #define __FEC_MXC_H +#include <clk.h> + /* Layout description of the FEC */ struct ethernet_regs { /* [10:2]addr = 00 */ @@ -250,6 +252,9 @@ struct fec_priv { int phy_id; int (*mii_postcall)(int); #endif +#ifdef CONFIG_DM_REGULATOR + struct udevice *phy_supply; +#endif #ifdef CONFIG_DM_GPIO struct gpio_desc phy_reset_gpio; uint32_t reset_delay; @@ -257,6 +262,8 @@ struct fec_priv { #ifdef CONFIG_DM_ETH u32 interface; #endif + struct clk ipg_clk; + u32 clk_rate; }; void imx_get_mac_from_fuse(int dev_id, unsigned char *mac); diff --git a/drivers/net/mvpp2.c b/drivers/net/mvpp2.c index f34245ba77..9b3ab25c19 100644 --- a/drivers/net/mvpp2.c +++ b/drivers/net/mvpp2.c @@ -4792,9 +4792,9 @@ static int phy_info_parse(struct udevice *dev, struct mvpp2_port *port) static void mvpp2_gpio_init(struct mvpp2_port *port) { if (dm_gpio_is_valid(&port->phy_reset_gpio)) { - dm_gpio_set_value(&port->phy_reset_gpio, 0); - udelay(1000); dm_gpio_set_value(&port->phy_reset_gpio, 1); + mdelay(10); + dm_gpio_set_value(&port->phy_reset_gpio, 0); } if (dm_gpio_is_valid(&port->phy_tx_disable_gpio)) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index f5821dfed9..3dc0822d9c 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -64,8 +64,32 @@ endif # MV88E61XX_SWITCH config PHYLIB_10G bool "Generic 10G PHY support" -config PHY_AQUANTIA +menuconfig PHY_AQUANTIA bool "Aquantia Ethernet PHYs support" + select PHY_GIGE + select PHYLIB_10G + +config PHY_AQUANTIA_UPLOAD_FW + bool "Aquantia firmware loading support" + default n + depends on PHY_AQUANTIA + help + Aquantia PHYs use firmware which can be either loaded automatically + from storage directly attached to the phy or loaded by the boot loader + via MDIO commands. The firmware is loaded from a file, specified by + the PHY_AQUANTIA_FW_PART and PHY_AQUANTIA_FW_NAME options. + +config PHY_AQUANTIA_FW_PART + string "Aquantia firmware partition" + depends on PHY_AQUANTIA_UPLOAD_FW + help + Partition containing the firmware file. + +config PHY_AQUANTIA_FW_NAME + string "Aquantia firmware filename" + depends on PHY_AQUANTIA_UPLOAD_FW + help + Firmware filename. config PHY_ATHEROS bool "Atheros Ethernet PHYs support" diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index fe81217432..37749e0185 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -8,10 +8,10 @@ #include <common.h> #include <dm.h> #include <phy.h> - -#ifndef CONFIG_PHYLIB_10G -#error The Aquantia PHY needs 10G support -#endif +#include <crc.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <fs.h> #define AQUNTIA_10G_CTL 0x20 #define AQUNTIA_VENDOR_P1 0xc400 @@ -19,9 +19,253 @@ #define AQUNTIA_SPEED_LSB_MASK 0x2000 #define AQUNTIA_SPEED_MSB_MASK 0x40 +/* registers in MDIO_MMD_VEND1 region */ +#define GLOBAL_FIRMWARE_ID 0x20 +#define GLOBAL_FAULT 0xc850 +#define GLOBAL_RSTATUS_1 0xc885 + +#define GLOBAL_STANDARD_CONTROL 0x0 +#define SOFT_RESET BIT(15) +#define LOW_POWER BIT(11) + +#define MAILBOX_CONTROL 0x0200 +#define MAILBOX_EXECUTE BIT(15) +#define MAILBOX_WRITE BIT(14) +#define MAILBOX_RESET_CRC BIT(12) +#define MAILBOX_BUSY BIT(8) + +#define MAILBOX_CRC 0x0201 + +#define MAILBOX_ADDR_MSW 0x0202 +#define MAILBOX_ADDR_LSW 0x0203 + +#define MAILBOX_DATA_MSW 0x0204 +#define MAILBOX_DATA_LSW 0x0205 + +#define UP_CONTROL 0xc001 +#define UP_RESET BIT(15) +#define UP_RUN_STALL_OVERRIDE BIT(6) +#define UP_RUN_STALL BIT(0) + +/* addresses of memory segments in the phy */ +#define DRAM_BASE_ADDR 0x3FFE0000 +#define IRAM_BASE_ADDR 0x40000000 + +/* firmware image format constants */ +#define VERSION_STRING_SIZE 0x40 +#define VERSION_STRING_OFFSET 0x0200 +#define HEADER_OFFSET 0x300 + +#pragma pack(1) +struct fw_header { + u8 padding[4]; + u8 iram_offset[3]; + u8 iram_size[3]; + u8 dram_offset[3]; + u8 dram_size[3]; +}; + +#pragma pack() + +#if defined(CONFIG_PHY_AQUANTIA_UPLOAD_FW) +static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length) +{ + loff_t length, read; + int ret; + void *addr = NULL; + + *fw_addr = NULL; + *fw_length = 0; + debug("Loading Acquantia microcode from %s %s\n", + CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME); + ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY); + if (ret < 0) + goto cleanup; + + ret = fs_size(CONFIG_PHY_AQUANTIA_FW_NAME, &length); + if (ret < 0) + goto cleanup; + + addr = malloc(length); + if (!addr) { + ret = -ENOMEM; + goto cleanup; + } + + ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY); + if (ret < 0) + goto cleanup; + + ret = fs_read(CONFIG_PHY_AQUANTIA_FW_NAME, (ulong)addr, 0, length, + &read); + if (ret < 0) + goto cleanup; + + *fw_addr = addr; + *fw_length = length; + debug("Found Acquantia microcode.\n"); + +cleanup: + if (ret < 0) { + printf("loading firmware file %s %s failed with error %d\n", + CONFIG_PHY_AQUANTIA_FW_PART, + CONFIG_PHY_AQUANTIA_FW_NAME, ret); + free(addr); + } + return ret; +} + +/* load data into the phy's memory */ +static int aquantia_load_memory(struct phy_device *phydev, u32 addr, + const u8 *data, size_t len) +{ + size_t pos; + u16 crc = 0, up_crc; + + phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_CONTROL, MAILBOX_RESET_CRC); + phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_ADDR_MSW, addr >> 16); + phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_ADDR_LSW, addr & 0xfffc); + + for (pos = 0; pos < len; pos += min(sizeof(u32), len - pos)) { + u32 word = 0; + + memcpy(&word, &data[pos], min(sizeof(u32), len - pos)); + + phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_DATA_MSW, + (word >> 16)); + phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_DATA_LSW, + word & 0xffff); + + phy_write(phydev, MDIO_MMD_VEND1, MAILBOX_CONTROL, + MAILBOX_EXECUTE | MAILBOX_WRITE); + + /* keep a big endian CRC to match the phy processor */ + word = cpu_to_be32(word); + crc = crc16_ccitt(crc, (u8 *)&word, sizeof(word)); + } + + up_crc = phy_read(phydev, MDIO_MMD_VEND1, MAILBOX_CRC); + if (crc != up_crc) { + printf("%s crc mismatch: calculated 0x%04hx phy 0x%04hx\n", + phydev->dev->name, crc, up_crc); + return -EINVAL; + } + return 0; +} + +static u32 unpack_u24(const u8 *data) +{ + return (data[2] << 16) + (data[1] << 8) + data[0]; +} + +static int aquantia_upload_firmware(struct phy_device *phydev) +{ + int ret; + u8 *addr = NULL; + size_t fw_length = 0; + u16 calculated_crc, read_crc; + char version[VERSION_STRING_SIZE]; + u32 primary_offset, iram_offset, iram_size, dram_offset, dram_size; + const struct fw_header *header; + + ret = aquantia_read_fw(&addr, &fw_length); + if (ret != 0) + return ret; + + read_crc = (addr[fw_length - 2] << 8) | addr[fw_length - 1]; + calculated_crc = crc16_ccitt(0, addr, fw_length - 2); + if (read_crc != calculated_crc) { + printf("%s bad firmware crc: file 0x%04x calculated 0x%04x\n", + phydev->dev->name, read_crc, calculated_crc); + ret = -EINVAL; + goto done; + } + + /* Find the DRAM and IRAM sections within the firmware file. */ + primary_offset = ((addr[9] & 0xf) << 8 | addr[8]) << 12; + + header = (struct fw_header *)&addr[primary_offset + HEADER_OFFSET]; + + iram_offset = primary_offset + unpack_u24(header->iram_offset); + iram_size = unpack_u24(header->iram_size); + + dram_offset = primary_offset + unpack_u24(header->dram_offset); + dram_size = unpack_u24(header->dram_size); + + debug("primary %d iram offset=%d size=%d dram offset=%d size=%d\n", + primary_offset, iram_offset, iram_size, dram_offset, dram_size); + + strlcpy(version, (char *)&addr[dram_offset + VERSION_STRING_OFFSET], + VERSION_STRING_SIZE); + printf("%s loading firmare version '%s'\n", phydev->dev->name, version); + + /* stall the microcprocessor */ + phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, + UP_RUN_STALL | UP_RUN_STALL_OVERRIDE); + + debug("loading dram 0x%08x from offset=%d size=%d\n", + DRAM_BASE_ADDR, dram_offset, dram_size); + ret = aquantia_load_memory(phydev, DRAM_BASE_ADDR, &addr[dram_offset], + dram_size); + if (ret != 0) + goto done; + + debug("loading iram 0x%08x from offset=%d size=%d\n", + IRAM_BASE_ADDR, iram_offset, iram_size); + ret = aquantia_load_memory(phydev, IRAM_BASE_ADDR, &addr[iram_offset], + iram_size); + if (ret != 0) + goto done; + + /* make sure soft reset and low power mode are clear */ + phy_write(phydev, MDIO_MMD_VEND1, GLOBAL_STANDARD_CONTROL, 0); + + /* Release the microprocessor. UP_RESET must be held for 100 usec. */ + phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, + UP_RUN_STALL | UP_RUN_STALL_OVERRIDE | UP_RESET); + + udelay(100); + + phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, UP_RUN_STALL_OVERRIDE); + + printf("%s firmare loading done.\n", phydev->dev->name); +done: + free(addr); + return ret; +} +#else +static int aquantia_upload_firmware(struct phy_device *phydev) +{ + printf("ERROR %s firmware loading disabled.\n", phydev->dev->name); + return -1; +} +#endif + int aquantia_config(struct phy_device *phydev) { - u32 val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR); + u32 val, id, rstatus, fault; + + id = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FIRMWARE_ID); + rstatus = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_RSTATUS_1); + fault = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FAULT); + + if (id != 0) + printf("%s running firmware version %X.%X.%X\n", + phydev->dev->name, (id >> 8), id & 0xff, + (rstatus >> 4) & 0xf); + + if (fault != 0) + printf("%s fault 0x%04x detected\n", phydev->dev->name, fault); + + if (id == 0 || fault != 0) { + int ret; + + ret = aquantia_upload_firmware(phydev); + if (ret != 0) + return ret; + } + + val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR); if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { /* 1000BASE-T mode */ |