X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fsunxi%2Fpatches-4.9%2F0053-stmmac-form-4-13.patch;fp=target%2Flinux%2Fsunxi%2Fpatches-4.9%2F0053-stmmac-form-4-13.patch;h=f829b79dc3639a430b034b3ab17bb1c7efe237f0;hp=0000000000000000000000000000000000000000;hb=ed43a4d4ac195bf3c149805094b628a0d45f8880;hpb=a8f63a0717f553e0a1b37ee9212fc4cb2a801426 diff --git a/target/linux/sunxi/patches-4.9/0053-stmmac-form-4-13.patch b/target/linux/sunxi/patches-4.9/0053-stmmac-form-4-13.patch new file mode 100644 index 0000000000..f829b79dc3 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0053-stmmac-form-4-13.patch @@ -0,0 +1,1924 @@ +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -145,6 +145,17 @@ config DWMAC_SUNXI + This selects Allwinner SoC glue layer support for the + stmmac device driver. This driver is used for A20/A31 + GMAC ethernet controller. ++ ++config DWMAC_SUN8I ++ tristate "Allwinner sun8i GMAC support" ++ default ARCH_SUNXI ++ depends on OF && (ARCH_SUNXI || COMPILE_TEST) ++ ---help--- ++ Support for Allwinner H3 A83T A64 EMAC ethernet controllers. ++ ++ This selects Allwinner SoC glue layer support for the ++ stmmac device driver. This driver is used for H3/A83T/A64 ++ EMAC ethernet controller. + endif + + config STMMAC_PCI +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -16,6 +16,7 @@ obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-alt + obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o + obj-$(CONFIG_DWMAC_STM32) += dwmac-stm32.o + obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o ++obj-$(CONFIG_DWMAC_SUN8I) += dwmac-sun8i.o + obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o + obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o + stmmac-platform-objs:= stmmac_platform.o +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -549,9 +549,11 @@ extern const struct stmmac_hwtimestamp s + extern const struct stmmac_mode_ops dwmac4_ring_mode_ops; + + struct mac_link { +- int port; +- int duplex; +- int speed; ++ u32 speed_mask; ++ u32 speed10; ++ u32 speed100; ++ u32 speed1000; ++ u32 duplex; + }; + + struct mii_regs { +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +@@ -269,7 +269,10 @@ static int socfpga_dwmac_set_phy_mode(st + ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift); + ctrl |= val << reg_shift; + +- if (dwmac->f2h_ptp_ref_clk) { ++ if (dwmac->f2h_ptp_ref_clk || ++ phymode == PHY_INTERFACE_MODE_MII || ++ phymode == PHY_INTERFACE_MODE_GMII || ++ phymode == PHY_INTERFACE_MODE_SGMII) { + ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2); + regmap_read(sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG, + &module); +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +@@ -0,0 +1,1007 @@ ++/* ++ * dwmac-sun8i.c - Allwinner sun8i DWMAC specific glue layer ++ * ++ * Copyright (C) 2017 Corentin Labbe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "stmmac.h" ++#include "stmmac_platform.h" ++ ++/* General notes on dwmac-sun8i: ++ * Locking: no locking is necessary in this file because all necessary locking ++ * is done in the "stmmac files" ++ */ ++ ++/* struct emac_variant - Descrive dwmac-sun8i hardware variant ++ * @default_syscon_value: The default value of the EMAC register in syscon ++ * This value is used for disabling properly EMAC ++ * and used as a good starting value in case of the ++ * boot process(uboot) leave some stuff. ++ * @internal_phy: Does the MAC embed an internal PHY ++ * @support_mii: Does the MAC handle MII ++ * @support_rmii: Does the MAC handle RMII ++ * @support_rgmii: Does the MAC handle RGMII ++ */ ++struct emac_variant { ++ u32 default_syscon_value; ++ int internal_phy; ++ bool support_mii; ++ bool support_rmii; ++ bool support_rgmii; ++}; ++ ++/* struct sunxi_priv_data - hold all sunxi private data ++ * @tx_clk: reference to MAC TX clock ++ * @ephy_clk: reference to the optional EPHY clock for the internal PHY ++ * @regulator: reference to the optional regulator ++ * @rst_ephy: reference to the optional EPHY reset for the internal PHY ++ * @variant: reference to the current board variant ++ * @regmap: regmap for using the syscon ++ * @use_internal_phy: Does the current PHY choice imply using the internal PHY ++ */ ++struct sunxi_priv_data { ++ struct clk *tx_clk; ++ struct clk *ephy_clk; ++ struct regulator *regulator; ++ struct reset_control *rst_ephy; ++ const struct emac_variant *variant; ++ struct regmap *regmap; ++ bool use_internal_phy; ++}; ++ ++static const struct emac_variant emac_variant_h3 = { ++ .default_syscon_value = 0x58000, ++ .internal_phy = PHY_INTERFACE_MODE_MII, ++ .support_mii = true, ++ .support_rmii = true, ++ .support_rgmii = true ++}; ++ ++static const struct emac_variant emac_variant_v3s = { ++ .default_syscon_value = 0x38000, ++ .internal_phy = PHY_INTERFACE_MODE_MII, ++ .support_mii = true ++}; ++ ++static const struct emac_variant emac_variant_a83t = { ++ .default_syscon_value = 0, ++ .internal_phy = 0, ++ .support_mii = true, ++ .support_rgmii = true ++}; ++ ++static const struct emac_variant emac_variant_a64 = { ++ .default_syscon_value = 0, ++ .internal_phy = 0, ++ .support_mii = true, ++ .support_rmii = true, ++ .support_rgmii = true ++}; ++ ++#define EMAC_BASIC_CTL0 0x00 ++#define EMAC_BASIC_CTL1 0x04 ++#define EMAC_INT_STA 0x08 ++#define EMAC_INT_EN 0x0C ++#define EMAC_TX_CTL0 0x10 ++#define EMAC_TX_CTL1 0x14 ++#define EMAC_TX_FLOW_CTL 0x1C ++#define EMAC_TX_DESC_LIST 0x20 ++#define EMAC_RX_CTL0 0x24 ++#define EMAC_RX_CTL1 0x28 ++#define EMAC_RX_DESC_LIST 0x34 ++#define EMAC_RX_FRM_FLT 0x38 ++#define EMAC_MDIO_CMD 0x48 ++#define EMAC_MDIO_DATA 0x4C ++#define EMAC_MACADDR_HI(reg) (0x50 + (reg) * 8) ++#define EMAC_MACADDR_LO(reg) (0x54 + (reg) * 8) ++#define EMAC_TX_DMA_STA 0xB0 ++#define EMAC_TX_CUR_DESC 0xB4 ++#define EMAC_TX_CUR_BUF 0xB8 ++#define EMAC_RX_DMA_STA 0xC0 ++#define EMAC_RX_CUR_DESC 0xC4 ++#define EMAC_RX_CUR_BUF 0xC8 ++ ++/* Use in EMAC_BASIC_CTL0 */ ++#define EMAC_DUPLEX_FULL BIT(0) ++#define EMAC_LOOPBACK BIT(1) ++#define EMAC_SPEED_1000 0 ++#define EMAC_SPEED_100 (0x03 << 2) ++#define EMAC_SPEED_10 (0x02 << 2) ++ ++/* Use in EMAC_BASIC_CTL1 */ ++#define EMAC_BURSTLEN_SHIFT 24 ++ ++/* Used in EMAC_RX_FRM_FLT */ ++#define EMAC_FRM_FLT_RXALL BIT(0) ++#define EMAC_FRM_FLT_CTL BIT(13) ++#define EMAC_FRM_FLT_MULTICAST BIT(16) ++ ++/* Used in RX_CTL1*/ ++#define EMAC_RX_MD BIT(1) ++#define EMAC_RX_TH_MASK GENMASK(4, 5) ++#define EMAC_RX_TH_32 0 ++#define EMAC_RX_TH_64 (0x1 << 4) ++#define EMAC_RX_TH_96 (0x2 << 4) ++#define EMAC_RX_TH_128 (0x3 << 4) ++#define EMAC_RX_DMA_EN BIT(30) ++#define EMAC_RX_DMA_START BIT(31) ++ ++/* Used in TX_CTL1*/ ++#define EMAC_TX_MD BIT(1) ++#define EMAC_TX_NEXT_FRM BIT(2) ++#define EMAC_TX_TH_MASK GENMASK(8, 10) ++#define EMAC_TX_TH_64 0 ++#define EMAC_TX_TH_128 (0x1 << 8) ++#define EMAC_TX_TH_192 (0x2 << 8) ++#define EMAC_TX_TH_256 (0x3 << 8) ++#define EMAC_TX_DMA_EN BIT(30) ++#define EMAC_TX_DMA_START BIT(31) ++ ++/* Used in RX_CTL0 */ ++#define EMAC_RX_RECEIVER_EN BIT(31) ++#define EMAC_RX_DO_CRC BIT(27) ++#define EMAC_RX_FLOW_CTL_EN BIT(16) ++ ++/* Used in TX_CTL0 */ ++#define EMAC_TX_TRANSMITTER_EN BIT(31) ++ ++/* Used in EMAC_TX_FLOW_CTL */ ++#define EMAC_TX_FLOW_CTL_EN BIT(0) ++ ++/* Used in EMAC_INT_STA */ ++#define EMAC_TX_INT BIT(0) ++#define EMAC_TX_DMA_STOP_INT BIT(1) ++#define EMAC_TX_BUF_UA_INT BIT(2) ++#define EMAC_TX_TIMEOUT_INT BIT(3) ++#define EMAC_TX_UNDERFLOW_INT BIT(4) ++#define EMAC_TX_EARLY_INT BIT(5) ++#define EMAC_RX_INT BIT(8) ++#define EMAC_RX_BUF_UA_INT BIT(9) ++#define EMAC_RX_DMA_STOP_INT BIT(10) ++#define EMAC_RX_TIMEOUT_INT BIT(11) ++#define EMAC_RX_OVERFLOW_INT BIT(12) ++#define EMAC_RX_EARLY_INT BIT(13) ++#define EMAC_RGMII_STA_INT BIT(16) ++ ++#define MAC_ADDR_TYPE_DST BIT(31) ++ ++/* H3 specific bits for EPHY */ ++#define H3_EPHY_ADDR_SHIFT 20 ++#define H3_EPHY_CLK_SEL BIT(18) /* 1: 24MHz, 0: 25MHz */ ++#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ ++#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ ++#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ ++ ++/* H3/A64 specific bits */ ++#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ ++ ++/* Generic system control EMAC_CLK bits */ ++#define SYSCON_ETXDC_MASK GENMASK(2, 0) ++#define SYSCON_ETXDC_SHIFT 10 ++#define SYSCON_ERXDC_MASK GENMASK(4, 0) ++#define SYSCON_ERXDC_SHIFT 5 ++/* EMAC PHY Interface Type */ ++#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */ ++#define SYSCON_ETCS_MASK GENMASK(1, 0) ++#define SYSCON_ETCS_MII 0x0 ++#define SYSCON_ETCS_EXT_GMII 0x1 ++#define SYSCON_ETCS_INT_GMII 0x2 ++#define SYSCON_EMAC_REG 0x30 ++ ++/* sun8i_dwmac_dma_reset() - reset the EMAC ++ * Called from stmmac via stmmac_dma_ops->reset ++ */ ++static int sun8i_dwmac_dma_reset(void __iomem *ioaddr) ++{ ++ writel(0, ioaddr + EMAC_RX_CTL1); ++ writel(0, ioaddr + EMAC_TX_CTL1); ++ writel(0, ioaddr + EMAC_RX_FRM_FLT); ++ writel(0, ioaddr + EMAC_RX_DESC_LIST); ++ writel(0, ioaddr + EMAC_TX_DESC_LIST); ++ writel(0, ioaddr + EMAC_INT_EN); ++ writel(0x1FFFFFF, ioaddr + EMAC_INT_STA); ++ return 0; ++} ++ ++/* sun8i_dwmac_dma_init() - initialize the EMAC ++ * Called from stmmac via stmmac_dma_ops->init ++ */ ++static void sun8i_dwmac_dma_init(void __iomem *ioaddr, ++ struct stmmac_dma_cfg *dma_cfg, ++ u32 dma_tx, u32 dma_rx, int atds) ++{ ++ /* Write TX and RX descriptors address */ ++ writel(dma_rx, ioaddr + EMAC_RX_DESC_LIST); ++ writel(dma_tx, ioaddr + EMAC_TX_DESC_LIST); ++ ++ writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN); ++ writel(0x1FFFFFF, ioaddr + EMAC_INT_STA); ++} ++ ++/* sun8i_dwmac_dump_regs() - Dump EMAC address space ++ * Called from stmmac_dma_ops->dump_regs ++ * Used for ethtool ++ */ ++static void sun8i_dwmac_dump_regs(void __iomem *ioaddr, u32 *reg_space) ++{ ++ int i; ++ ++ for (i = 0; i < 0xC8; i += 4) { ++ if (i == 0x32 || i == 0x3C) ++ continue; ++ reg_space[i / 4] = readl(ioaddr + i); ++ } ++} ++ ++/* sun8i_dwmac_dump_mac_regs() - Dump EMAC address space ++ * Called from stmmac_ops->dump_regs ++ * Used for ethtool ++ */ ++static void sun8i_dwmac_dump_mac_regs(struct mac_device_info *hw, ++ u32 *reg_space) ++{ ++ int i; ++ void __iomem *ioaddr = hw->pcsr; ++ ++ for (i = 0; i < 0xC8; i += 4) { ++ if (i == 0x32 || i == 0x3C) ++ continue; ++ reg_space[i / 4] = readl(ioaddr + i); ++ } ++} ++ ++static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan) ++{ ++ writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN); ++} ++ ++static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan) ++{ ++ writel(0, ioaddr + EMAC_INT_EN); ++} ++ ++static void sun8i_dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan) ++{ ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_TX_CTL1); ++ v |= EMAC_TX_DMA_START; ++ v |= EMAC_TX_DMA_EN; ++ writel(v, ioaddr + EMAC_TX_CTL1); ++} ++ ++static void sun8i_dwmac_enable_dma_transmission(void __iomem *ioaddr) ++{ ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_TX_CTL1); ++ v |= EMAC_TX_DMA_START; ++ v |= EMAC_TX_DMA_EN; ++ writel(v, ioaddr + EMAC_TX_CTL1); ++} ++ ++static void sun8i_dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan) ++{ ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_TX_CTL1); ++ v &= ~EMAC_TX_DMA_EN; ++ writel(v, ioaddr + EMAC_TX_CTL1); ++} ++ ++static void sun8i_dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan) ++{ ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_RX_CTL1); ++ v |= EMAC_RX_DMA_START; ++ v |= EMAC_RX_DMA_EN; ++ writel(v, ioaddr + EMAC_RX_CTL1); ++} ++ ++static void sun8i_dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan) ++{ ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_RX_CTL1); ++ v &= ~EMAC_RX_DMA_EN; ++ writel(v, ioaddr + EMAC_RX_CTL1); ++} ++ ++static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr, ++ struct stmmac_extra_stats *x, u32 chan) ++{ ++ u32 v; ++ int ret = 0; ++ ++ v = readl(ioaddr + EMAC_INT_STA); ++ ++ if (v & EMAC_TX_INT) { ++ ret |= handle_tx; ++ x->tx_normal_irq_n++; ++ } ++ ++ if (v & EMAC_TX_DMA_STOP_INT) ++ x->tx_process_stopped_irq++; ++ ++ if (v & EMAC_TX_BUF_UA_INT) ++ x->tx_process_stopped_irq++; ++ ++ if (v & EMAC_TX_TIMEOUT_INT) ++ ret |= tx_hard_error; ++ ++ if (v & EMAC_TX_UNDERFLOW_INT) { ++ ret |= tx_hard_error; ++ x->tx_undeflow_irq++; ++ } ++ ++ if (v & EMAC_TX_EARLY_INT) ++ x->tx_early_irq++; ++ ++ if (v & EMAC_RX_INT) { ++ ret |= handle_rx; ++ x->rx_normal_irq_n++; ++ } ++ ++ if (v & EMAC_RX_BUF_UA_INT) ++ x->rx_buf_unav_irq++; ++ ++ if (v & EMAC_RX_DMA_STOP_INT) ++ x->rx_process_stopped_irq++; ++ ++ if (v & EMAC_RX_TIMEOUT_INT) ++ ret |= tx_hard_error; ++ ++ if (v & EMAC_RX_OVERFLOW_INT) { ++ ret |= tx_hard_error; ++ x->rx_overflow_irq++; ++ } ++ ++ if (v & EMAC_RX_EARLY_INT) ++ x->rx_early_irq++; ++ ++ if (v & EMAC_RGMII_STA_INT) ++ x->irq_rgmii_n++; ++ ++ writel(v, ioaddr + EMAC_INT_STA); ++ ++ return ret; ++} ++ ++static void sun8i_dwmac_dma_operation_mode(void __iomem *ioaddr, int txmode, ++ int rxmode, int rxfifosz) ++{ ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_TX_CTL1); ++ if (txmode == SF_DMA_MODE) { ++ v |= EMAC_TX_MD; ++ /* Undocumented bit (called TX_NEXT_FRM in BSP), the original ++ * comment is ++ * "Operating on second frame increase the performance ++ * especially when transmit store-and-forward is used." ++ */ ++ v |= EMAC_TX_NEXT_FRM; ++ } else { ++ v &= ~EMAC_TX_MD; ++ v &= ~EMAC_TX_TH_MASK; ++ if (txmode < 64) ++ v |= EMAC_TX_TH_64; ++ else if (txmode < 128) ++ v |= EMAC_TX_TH_128; ++ else if (txmode < 192) ++ v |= EMAC_TX_TH_192; ++ else if (txmode < 256) ++ v |= EMAC_TX_TH_256; ++ } ++ writel(v, ioaddr + EMAC_TX_CTL1); ++ ++ v = readl(ioaddr + EMAC_RX_CTL1); ++ if (rxmode == SF_DMA_MODE) { ++ v |= EMAC_RX_MD; ++ } else { ++ v &= ~EMAC_RX_MD; ++ v &= ~EMAC_RX_TH_MASK; ++ if (rxmode < 32) ++ v |= EMAC_RX_TH_32; ++ else if (rxmode < 64) ++ v |= EMAC_RX_TH_64; ++ else if (rxmode < 96) ++ v |= EMAC_RX_TH_96; ++ else if (rxmode < 128) ++ v |= EMAC_RX_TH_128; ++ } ++ writel(v, ioaddr + EMAC_RX_CTL1); ++} ++ ++static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = { ++ .reset = sun8i_dwmac_dma_reset, ++ .init = sun8i_dwmac_dma_init, ++ .dump_regs = sun8i_dwmac_dump_regs, ++ .dma_mode = sun8i_dwmac_dma_operation_mode, ++ .enable_dma_transmission = sun8i_dwmac_enable_dma_transmission, ++ .enable_dma_irq = sun8i_dwmac_enable_dma_irq, ++ .disable_dma_irq = sun8i_dwmac_disable_dma_irq, ++ .start_tx = sun8i_dwmac_dma_start_tx, ++ .stop_tx = sun8i_dwmac_dma_stop_tx, ++ .start_rx = sun8i_dwmac_dma_start_rx, ++ .stop_rx = sun8i_dwmac_dma_stop_rx, ++ .dma_interrupt = sun8i_dwmac_dma_interrupt, ++}; ++ ++static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) ++{ ++ struct sunxi_priv_data *gmac = priv; ++ int ret; ++ ++ if (gmac->regulator) { ++ ret = regulator_enable(gmac->regulator); ++ if (ret) { ++ dev_err(&pdev->dev, "Fail to enable regulator\n"); ++ return ret; ++ } ++ } ++ ++ ret = clk_prepare_enable(gmac->tx_clk); ++ if (ret) { ++ if (gmac->regulator) ++ regulator_disable(gmac->regulator); ++ dev_err(&pdev->dev, "Could not enable AHB clock\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void sun8i_dwmac_core_init(struct mac_device_info *hw, int mtu) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ ++ v = (8 << EMAC_BURSTLEN_SHIFT); /* burst len */ ++ writel(v, ioaddr + EMAC_BASIC_CTL1); ++} ++ ++static void sun8i_dwmac_set_mac(void __iomem *ioaddr, bool enable) ++{ ++ u32 t, r; ++ ++ t = readl(ioaddr + EMAC_TX_CTL0); ++ r = readl(ioaddr + EMAC_RX_CTL0); ++ if (enable) { ++ t |= EMAC_TX_TRANSMITTER_EN; ++ r |= EMAC_RX_RECEIVER_EN; ++ } else { ++ t &= ~EMAC_TX_TRANSMITTER_EN; ++ r &= ~EMAC_RX_RECEIVER_EN; ++ } ++ writel(t, ioaddr + EMAC_TX_CTL0); ++ writel(r, ioaddr + EMAC_RX_CTL0); ++} ++ ++/* Set MAC address at slot reg_n ++ * All slot > 0 need to be enabled with MAC_ADDR_TYPE_DST ++ * If addr is NULL, clear the slot ++ */ ++static void sun8i_dwmac_set_umac_addr(struct mac_device_info *hw, ++ unsigned char *addr, ++ unsigned int reg_n) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ ++ if (!addr) { ++ writel(0, ioaddr + EMAC_MACADDR_HI(reg_n)); ++ return; ++ } ++ ++ stmmac_set_mac_addr(ioaddr, addr, EMAC_MACADDR_HI(reg_n), ++ EMAC_MACADDR_LO(reg_n)); ++ if (reg_n > 0) { ++ v = readl(ioaddr + EMAC_MACADDR_HI(reg_n)); ++ v |= MAC_ADDR_TYPE_DST; ++ writel(v, ioaddr + EMAC_MACADDR_HI(reg_n)); ++ } ++} ++ ++static void sun8i_dwmac_get_umac_addr(struct mac_device_info *hw, ++ unsigned char *addr, ++ unsigned int reg_n) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ ++ stmmac_get_mac_addr(ioaddr, addr, EMAC_MACADDR_HI(reg_n), ++ EMAC_MACADDR_LO(reg_n)); ++} ++ ++/* caution this function must return non 0 to work */ ++static int sun8i_dwmac_rx_ipc_enable(struct mac_device_info *hw) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_RX_CTL0); ++ v |= EMAC_RX_DO_CRC; ++ writel(v, ioaddr + EMAC_RX_CTL0); ++ ++ return 1; ++} ++ ++static void sun8i_dwmac_set_filter(struct mac_device_info *hw, ++ struct net_device *dev) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ int i = 1; ++ struct netdev_hw_addr *ha; ++ int macaddrs = netdev_uc_count(dev) + netdev_mc_count(dev) + 1; ++ ++ v = EMAC_FRM_FLT_CTL; ++ ++ if (dev->flags & IFF_PROMISC) { ++ v = EMAC_FRM_FLT_RXALL; ++ } else if (dev->flags & IFF_ALLMULTI) { ++ v |= EMAC_FRM_FLT_MULTICAST; ++ } else if (macaddrs <= hw->unicast_filter_entries) { ++ if (!netdev_mc_empty(dev)) { ++ netdev_for_each_mc_addr(ha, dev) { ++ sun8i_dwmac_set_umac_addr(hw, ha->addr, i); ++ i++; ++ } ++ } ++ if (!netdev_uc_empty(dev)) { ++ netdev_for_each_uc_addr(ha, dev) { ++ sun8i_dwmac_set_umac_addr(hw, ha->addr, i); ++ i++; ++ } ++ } ++ } else { ++ netdev_info(dev, "Too many address, switching to promiscuous\n"); ++ v = EMAC_FRM_FLT_RXALL; ++ } ++ ++ /* Disable unused address filter slots */ ++ while (i < hw->unicast_filter_entries) ++ sun8i_dwmac_set_umac_addr(hw, NULL, i++); ++ ++ writel(v, ioaddr + EMAC_RX_FRM_FLT); ++} ++ ++static void sun8i_dwmac_flow_ctrl(struct mac_device_info *hw, ++ unsigned int duplex, unsigned int fc, ++ unsigned int pause_time, u32 tx_cnt) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_RX_CTL0); ++ if (fc == FLOW_AUTO) ++ v |= EMAC_RX_FLOW_CTL_EN; ++ else ++ v &= ~EMAC_RX_FLOW_CTL_EN; ++ writel(v, ioaddr + EMAC_RX_CTL0); ++ ++ v = readl(ioaddr + EMAC_TX_FLOW_CTL); ++ if (fc == FLOW_AUTO) ++ v |= EMAC_TX_FLOW_CTL_EN; ++ else ++ v &= ~EMAC_TX_FLOW_CTL_EN; ++ writel(v, ioaddr + EMAC_TX_FLOW_CTL); ++} ++ ++static int sun8i_dwmac_reset(struct stmmac_priv *priv) ++{ ++ u32 v; ++ int err; ++ ++ v = readl(priv->ioaddr + EMAC_BASIC_CTL1); ++ writel(v | 0x01, priv->ioaddr + EMAC_BASIC_CTL1); ++ ++ /* The timeout was previoulsy set to 10ms, but some board (OrangePI0) ++ * need more if no cable plugged. 100ms seems OK ++ */ ++ err = readl_poll_timeout(priv->ioaddr + EMAC_BASIC_CTL1, v, ++ !(v & 0x01), 100, 100000); ++ ++ if (err) { ++ dev_err(priv->device, "EMAC reset timeout\n"); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) ++{ ++ struct sunxi_priv_data *gmac = priv->plat->bsp_priv; ++ struct device_node *node = priv->device->of_node; ++ int ret; ++ u32 reg, val; ++ ++ regmap_read(gmac->regmap, SYSCON_EMAC_REG, &val); ++ reg = gmac->variant->default_syscon_value; ++ if (reg != val) ++ dev_warn(priv->device, ++ "Current syscon value is not the default %x (expect %x)\n", ++ val, reg); ++ ++ if (gmac->variant->internal_phy) { ++ if (!gmac->use_internal_phy) { ++ /* switch to external PHY interface */ ++ reg &= ~H3_EPHY_SELECT; ++ } else { ++ reg |= H3_EPHY_SELECT; ++ reg &= ~H3_EPHY_SHUTDOWN; ++ dev_dbg(priv->device, "Select internal_phy %x\n", reg); ++ ++ if (of_property_read_bool(priv->plat->phy_node, ++ "allwinner,leds-active-low")) ++ reg |= H3_EPHY_LED_POL; ++ else ++ reg &= ~H3_EPHY_LED_POL; ++ ++ /* Force EPHY xtal frequency to 24MHz. */ ++ reg |= H3_EPHY_CLK_SEL; ++ ++ ret = of_mdio_parse_addr(priv->device, ++ priv->plat->phy_node); ++ if (ret < 0) { ++ dev_err(priv->device, "Could not parse MDIO addr\n"); ++ return ret; ++ } ++ /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY ++ * address. No need to mask it again. ++ */ ++ reg |= ret << H3_EPHY_ADDR_SHIFT; ++ } ++ } ++ ++ if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) { ++ if (val % 100) { ++ dev_err(priv->device, "tx-delay must be a multiple of 100\n"); ++ return -EINVAL; ++ } ++ val /= 100; ++ dev_dbg(priv->device, "set tx-delay to %x\n", val); ++ if (val <= SYSCON_ETXDC_MASK) { ++ reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT); ++ reg |= (val << SYSCON_ETXDC_SHIFT); ++ } else { ++ dev_err(priv->device, "Invalid TX clock delay: %d\n", ++ val); ++ return -EINVAL; ++ } ++ } ++ ++ if (!of_property_read_u32(node, "allwinner,rx-delay-ps", &val)) { ++ if (val % 100) { ++ dev_err(priv->device, "rx-delay must be a multiple of 100\n"); ++ return -EINVAL; ++ } ++ val /= 100; ++ dev_dbg(priv->device, "set rx-delay to %x\n", val); ++ if (val <= SYSCON_ERXDC_MASK) { ++ reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT); ++ reg |= (val << SYSCON_ERXDC_SHIFT); ++ } else { ++ dev_err(priv->device, "Invalid RX clock delay: %d\n", ++ val); ++ return -EINVAL; ++ } ++ } ++ ++ /* Clear interface mode bits */ ++ reg &= ~(SYSCON_ETCS_MASK | SYSCON_EPIT); ++ if (gmac->variant->support_rmii) ++ reg &= ~SYSCON_RMII_EN; ++ ++ switch (priv->plat->interface) { ++ case PHY_INTERFACE_MODE_MII: ++ /* default */ ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII; ++ break; ++ default: ++ dev_err(priv->device, "Unsupported interface mode: %s", ++ phy_modes(priv->plat->interface)); ++ return -EINVAL; ++ } ++ ++ regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg); ++ ++ return 0; ++} ++ ++static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac) ++{ ++ u32 reg = gmac->variant->default_syscon_value; ++ ++ regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg); ++} ++ ++static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv) ++{ ++ struct sunxi_priv_data *gmac = priv->plat->bsp_priv; ++ int ret; ++ ++ if (!gmac->use_internal_phy) ++ return 0; ++ ++ ret = clk_prepare_enable(gmac->ephy_clk); ++ if (ret) { ++ dev_err(priv->device, "Cannot enable ephy\n"); ++ return ret; ++ } ++ ++ /* Make sure the EPHY is properly reseted, as U-Boot may leave ++ * it at deasserted state, and thus it may fail to reset EMAC. ++ */ ++ reset_control_assert(gmac->rst_ephy); ++ ++ ret = reset_control_deassert(gmac->rst_ephy); ++ if (ret) { ++ dev_err(priv->device, "Cannot deassert ephy\n"); ++ clk_disable_unprepare(gmac->ephy_clk); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac) ++{ ++ if (!gmac->use_internal_phy) ++ return 0; ++ ++ clk_disable_unprepare(gmac->ephy_clk); ++ reset_control_assert(gmac->rst_ephy); ++ return 0; ++} ++ ++/* sun8i_power_phy() - Activate the PHY: ++ * In case of error, no need to call sun8i_unpower_phy(), ++ * it will be called anyway by sun8i_dwmac_exit() ++ */ ++static int sun8i_power_phy(struct stmmac_priv *priv) ++{ ++ int ret; ++ ++ ret = sun8i_dwmac_power_internal_phy(priv); ++ if (ret) ++ return ret; ++ ++ ret = sun8i_dwmac_set_syscon(priv); ++ if (ret) ++ return ret; ++ ++ /* After changing syscon value, the MAC need reset or it will use ++ * the last value (and so the last PHY set. ++ */ ++ ret = sun8i_dwmac_reset(priv); ++ if (ret) ++ return ret; ++ return 0; ++} ++ ++static void sun8i_unpower_phy(struct sunxi_priv_data *gmac) ++{ ++ sun8i_dwmac_unset_syscon(gmac); ++ sun8i_dwmac_unpower_internal_phy(gmac); ++} ++ ++static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) ++{ ++ struct sunxi_priv_data *gmac = priv; ++ ++ sun8i_unpower_phy(gmac); ++ ++ clk_disable_unprepare(gmac->tx_clk); ++ ++ if (gmac->regulator) ++ regulator_disable(gmac->regulator); ++} ++ ++static const struct stmmac_ops sun8i_dwmac_ops = { ++ .core_init = sun8i_dwmac_core_init, ++ .set_mac = sun8i_dwmac_set_mac, ++ .dump_regs = sun8i_dwmac_dump_mac_regs, ++ .rx_ipc = sun8i_dwmac_rx_ipc_enable, ++ .set_filter = sun8i_dwmac_set_filter, ++ .flow_ctrl = sun8i_dwmac_flow_ctrl, ++ .set_umac_addr = sun8i_dwmac_set_umac_addr, ++ .get_umac_addr = sun8i_dwmac_get_umac_addr, ++}; ++ ++static struct mac_device_info *sun8i_dwmac_setup(void *ppriv) ++{ ++ struct mac_device_info *mac; ++ struct stmmac_priv *priv = ppriv; ++ int ret; ++ ++ mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); ++ if (!mac) ++ return NULL; ++ ++ ret = sun8i_power_phy(priv); ++ if (ret) ++ return NULL; ++ ++ mac->pcsr = priv->ioaddr; ++ mac->mac = &sun8i_dwmac_ops; ++ mac->dma = &sun8i_dwmac_dma_ops; ++ ++ /* The loopback bit seems to be re-set when link change ++ * Simply mask it each time ++ * Speed 10/100/1000 are set in BIT(2)/BIT(3) ++ */ ++ mac->link.speed_mask = GENMASK(3, 2) | EMAC_LOOPBACK; ++ mac->link.speed10 = EMAC_SPEED_10; ++ mac->link.speed100 = EMAC_SPEED_100; ++ mac->link.speed1000 = EMAC_SPEED_1000; ++ mac->link.duplex = EMAC_DUPLEX_FULL; ++ mac->mii.addr = EMAC_MDIO_CMD; ++ mac->mii.data = EMAC_MDIO_DATA; ++ mac->mii.reg_shift = 4; ++ mac->mii.reg_mask = GENMASK(8, 4); ++ mac->mii.addr_shift = 12; ++ mac->mii.addr_mask = GENMASK(16, 12); ++ mac->mii.clk_csr_shift = 20; ++ mac->mii.clk_csr_mask = GENMASK(22, 20); ++ mac->unicast_filter_entries = 8; ++ ++ /* Synopsys Id is not available */ ++ priv->synopsys_id = 0; ++ ++ return mac; ++} ++ ++static int sun8i_dwmac_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ struct sunxi_priv_data *gmac; ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ ret = stmmac_get_platform_resources(pdev, &stmmac_res); ++ if (ret) ++ return ret; ++ ++ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); ++ if (IS_ERR(plat_dat)) ++ return PTR_ERR(plat_dat); ++ ++ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); ++ if (!gmac) ++ return -ENOMEM; ++ ++ gmac->variant = of_device_get_match_data(&pdev->dev); ++ if (!gmac->variant) { ++ dev_err(&pdev->dev, "Missing dwmac-sun8i variant\n"); ++ return -EINVAL; ++ } ++ ++ gmac->tx_clk = devm_clk_get(dev, "stmmaceth"); ++ if (IS_ERR(gmac->tx_clk)) { ++ dev_err(dev, "Could not get TX clock\n"); ++ return PTR_ERR(gmac->tx_clk); ++ } ++ ++ /* Optional regulator for PHY */ ++ gmac->regulator = devm_regulator_get_optional(dev, "phy"); ++ if (IS_ERR(gmac->regulator)) { ++ if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_info(dev, "No regulator found\n"); ++ gmac->regulator = NULL; ++ } ++ ++ gmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "syscon"); ++ if (IS_ERR(gmac->regmap)) { ++ ret = PTR_ERR(gmac->regmap); ++ dev_err(&pdev->dev, "Unable to map syscon: %d\n", ret); ++ return ret; ++ } ++ ++ plat_dat->interface = of_get_phy_mode(dev->of_node); ++ if (plat_dat->interface == gmac->variant->internal_phy) { ++ dev_info(&pdev->dev, "Will use internal PHY\n"); ++ gmac->use_internal_phy = true; ++ gmac->ephy_clk = of_clk_get(plat_dat->phy_node, 0); ++ if (IS_ERR(gmac->ephy_clk)) { ++ ret = PTR_ERR(gmac->ephy_clk); ++ dev_err(&pdev->dev, "Cannot get EPHY clock: %d\n", ret); ++ return -EINVAL; ++ } ++ ++ gmac->rst_ephy = of_reset_control_get(plat_dat->phy_node, NULL); ++ if (IS_ERR(gmac->rst_ephy)) { ++ ret = PTR_ERR(gmac->rst_ephy); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ dev_err(&pdev->dev, "No EPHY reset control found %d\n", ++ ret); ++ return -EINVAL; ++ } ++ } else { ++ dev_info(&pdev->dev, "Will use external PHY\n"); ++ gmac->use_internal_phy = false; ++ } ++ ++ /* platform data specifying hardware features and callbacks. ++ * hardware features were copied from Allwinner drivers. ++ */ ++ plat_dat->rx_coe = STMMAC_RX_COE_TYPE2; ++ plat_dat->tx_coe = 1; ++ plat_dat->has_sun8i = true; ++ plat_dat->bsp_priv = gmac; ++ plat_dat->init = sun8i_dwmac_init; ++ plat_dat->exit = sun8i_dwmac_exit; ++ plat_dat->setup = sun8i_dwmac_setup; ++ ++ ret = sun8i_dwmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; ++ ++ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++ if (ret) ++ sun8i_dwmac_exit(pdev, plat_dat->bsp_priv); ++ ++ return ret; ++} ++ ++static const struct of_device_id sun8i_dwmac_match[] = { ++ { .compatible = "allwinner,sun8i-h3-emac", ++ .data = &emac_variant_h3 }, ++ { .compatible = "allwinner,sun8i-v3s-emac", ++ .data = &emac_variant_v3s }, ++ { .compatible = "allwinner,sun8i-a83t-emac", ++ .data = &emac_variant_a83t }, ++ { .compatible = "allwinner,sun50i-a64-emac", ++ .data = &emac_variant_a64 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, sun8i_dwmac_match); ++ ++static struct platform_driver sun8i_dwmac_driver = { ++ .probe = sun8i_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "dwmac-sun8i", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = sun8i_dwmac_match, ++ }, ++}; ++module_platform_driver(sun8i_dwmac_driver); ++ ++MODULE_AUTHOR("Corentin Labbe "); ++MODULE_DESCRIPTION("Allwinner sun8i DWMAC specific glue layer"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +@@ -45,15 +45,17 @@ static void dwmac1000_core_init(struct m + if (hw->ps) { + value |= GMAC_CONTROL_TE; + +- if (hw->ps == SPEED_1000) { +- value &= ~GMAC_CONTROL_PS; +- } else { +- value |= GMAC_CONTROL_PS; +- +- if (hw->ps == SPEED_10) +- value &= ~GMAC_CONTROL_FES; +- else +- value |= GMAC_CONTROL_FES; ++ value &= ~hw->link.speed_mask; ++ switch (hw->ps) { ++ case SPEED_1000: ++ value |= hw->link.speed1000; ++ break; ++ case SPEED_100: ++ value |= hw->link.speed100; ++ break; ++ case SPEED_10: ++ value |= hw->link.speed10; ++ break; + } + } + +@@ -531,9 +533,11 @@ struct mac_device_info *dwmac1000_setup( + mac->mac = &dwmac1000_ops; + mac->dma = &dwmac1000_dma_ops; + +- mac->link.port = GMAC_CONTROL_PS; + mac->link.duplex = GMAC_CONTROL_DM; +- mac->link.speed = GMAC_CONTROL_FES; ++ mac->link.speed10 = GMAC_CONTROL_PS; ++ mac->link.speed100 = GMAC_CONTROL_PS | GMAC_CONTROL_FES; ++ mac->link.speed1000 = 0; ++ mac->link.speed_mask = GMAC_CONTROL_PS | GMAC_CONTROL_FES; + mac->mii.addr = GMAC_MII_ADDR; + mac->mii.data = GMAC_MII_DATA; + mac->mii.addr_shift = 11; +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +@@ -205,8 +205,8 @@ static void dwmac1000_dump_dma_regs(void + { + int i; + +- for (i = 0; i < 22; i++) +- if ((i < 9) || (i > 17)) ++ for (i = 0; i < NUM_DWMAC1000_DMA_REGS; i++) ++ if ((i < 12) || (i > 17)) + reg_space[DMA_BUS_MODE / 4 + i] = + readl(ioaddr + DMA_BUS_MODE + i * 4); + } +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +@@ -175,9 +175,11 @@ struct mac_device_info *dwmac100_setup(v + mac->mac = &dwmac100_ops; + mac->dma = &dwmac100_dma_ops; + +- mac->link.port = MAC_CONTROL_PS; + mac->link.duplex = MAC_CONTROL_F; +- mac->link.speed = 0; ++ mac->link.speed10 = 0; ++ mac->link.speed100 = 0; ++ mac->link.speed1000 = 0; ++ mac->link.speed_mask = MAC_CONTROL_PS; + mac->mii.addr = MAC_MII_ADDR; + mac->mii.data = MAC_MII_DATA; + mac->mii.addr_shift = 11; +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +@@ -70,7 +70,7 @@ static void dwmac100_dump_dma_regs(void + { + int i; + +- for (i = 0; i < 9; i++) ++ for (i = 0; i < NUM_DWMAC100_DMA_REGS; i++) + reg_space[DMA_BUS_MODE / 4 + i] = + readl(ioaddr + DMA_BUS_MODE + i * 4); + +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -35,15 +35,17 @@ static void dwmac4_core_init(struct mac_ + if (hw->ps) { + value |= GMAC_CONFIG_TE; + +- if (hw->ps == SPEED_1000) { +- value &= ~GMAC_CONFIG_PS; +- } else { +- value |= GMAC_CONFIG_PS; +- +- if (hw->ps == SPEED_10) +- value &= ~GMAC_CONFIG_FES; +- else +- value |= GMAC_CONFIG_FES; ++ value &= hw->link.speed_mask; ++ switch (hw->ps) { ++ case SPEED_1000: ++ value |= hw->link.speed1000; ++ break; ++ case SPEED_100: ++ value |= hw->link.speed100; ++ break; ++ case SPEED_10: ++ value |= hw->link.speed10; ++ break; + } + } + +@@ -115,7 +117,7 @@ static void dwmac4_tx_queue_routing(stru + void __iomem *ioaddr = hw->pcsr; + u32 value; + +- const struct stmmac_rx_routing route_possibilities[] = { ++ static const struct stmmac_rx_routing route_possibilities[] = { + { GMAC_RXQCTRL_AVCPQ_MASK, GMAC_RXQCTRL_AVCPQ_SHIFT }, + { GMAC_RXQCTRL_PTPQ_MASK, GMAC_RXQCTRL_PTPQ_SHIFT }, + { GMAC_RXQCTRL_DCBCPQ_MASK, GMAC_RXQCTRL_DCBCPQ_SHIFT }, +@@ -747,9 +749,11 @@ struct mac_device_info *dwmac4_setup(voi + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + +- mac->link.port = GMAC_CONFIG_PS; + mac->link.duplex = GMAC_CONFIG_DM; +- mac->link.speed = GMAC_CONFIG_FES; ++ mac->link.speed10 = GMAC_CONFIG_PS; ++ mac->link.speed100 = GMAC_CONFIG_FES | GMAC_CONFIG_PS; ++ mac->link.speed1000 = 0; ++ mac->link.speed_mask = GMAC_CONFIG_FES | GMAC_CONFIG_PS; + mac->mii.addr = GMAC_MDIO_ADDR; + mac->mii.data = GMAC_MDIO_DATA; + mac->mii.addr_shift = 21; +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +@@ -71,9 +71,9 @@ static void dwmac4_dma_axi(void __iomem + writel(value, ioaddr + DMA_SYS_BUS_MODE); + } + +-void dwmac4_dma_init_rx_chan(void __iomem *ioaddr, +- struct stmmac_dma_cfg *dma_cfg, +- u32 dma_rx_phy, u32 chan) ++static void dwmac4_dma_init_rx_chan(void __iomem *ioaddr, ++ struct stmmac_dma_cfg *dma_cfg, ++ u32 dma_rx_phy, u32 chan) + { + u32 value; + u32 rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; +@@ -85,9 +85,9 @@ void dwmac4_dma_init_rx_chan(void __iome + writel(dma_rx_phy, ioaddr + DMA_CHAN_RX_BASE_ADDR(chan)); + } + +-void dwmac4_dma_init_tx_chan(void __iomem *ioaddr, +- struct stmmac_dma_cfg *dma_cfg, +- u32 dma_tx_phy, u32 chan) ++static void dwmac4_dma_init_tx_chan(void __iomem *ioaddr, ++ struct stmmac_dma_cfg *dma_cfg, ++ u32 dma_tx_phy, u32 chan) + { + u32 value; + u32 txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; +@@ -99,8 +99,8 @@ void dwmac4_dma_init_tx_chan(void __iome + writel(dma_tx_phy, ioaddr + DMA_CHAN_TX_BASE_ADDR(chan)); + } + +-void dwmac4_dma_init_channel(void __iomem *ioaddr, +- struct stmmac_dma_cfg *dma_cfg, u32 chan) ++static void dwmac4_dma_init_channel(void __iomem *ioaddr, ++ struct stmmac_dma_cfg *dma_cfg, u32 chan) + { + u32 value; + +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +@@ -136,6 +136,9 @@ + #define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ + #define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ + ++#define NUM_DWMAC100_DMA_REGS 9 ++#define NUM_DWMAC1000_DMA_REGS 23 ++ + void dwmac_enable_dma_transmission(void __iomem *ioaddr); + void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan); + void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +@@ -248,6 +248,7 @@ void stmmac_set_mac_addr(void __iomem *i + data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + writel(data, ioaddr + low); + } ++EXPORT_SYMBOL_GPL(stmmac_set_mac_addr); + + /* Enable disable MAC RX/TX */ + void stmmac_set_mac(void __iomem *ioaddr, bool enable) +@@ -279,4 +280,4 @@ void stmmac_get_mac_addr(void __iomem *i + addr[4] = hi_addr & 0xff; + addr[5] = (hi_addr >> 8) & 0xff; + } +- ++EXPORT_SYMBOL_GPL(stmmac_get_mac_addr); +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -104,7 +104,7 @@ struct stmmac_priv { + /* TX Queue */ + struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; + +- int oldlink; ++ bool oldlink; + int speed; + int oldduplex; + unsigned int flow_ctrl; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -29,10 +29,12 @@ + #include "stmmac.h" + #include "dwmac_dma.h" + +-#define REG_SPACE_SIZE 0x1054 ++#define REG_SPACE_SIZE 0x1060 + #define MAC100_ETHTOOL_NAME "st_mac100" + #define GMAC_ETHTOOL_NAME "st_gmac" + ++#define ETHTOOL_DMA_OFFSET 55 ++ + struct stmmac_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; +@@ -273,7 +275,6 @@ static int stmmac_ethtool_get_link_ksett + { + struct stmmac_priv *priv = netdev_priv(dev); + struct phy_device *phy = dev->phydev; +- int rc; + + if (priv->hw->pcs & STMMAC_PCS_RGMII || + priv->hw->pcs & STMMAC_PCS_SGMII) { +@@ -364,8 +365,8 @@ static int stmmac_ethtool_get_link_ksett + "link speed / duplex setting\n", dev->name); + return -EBUSY; + } +- rc = phy_ethtool_ksettings_get(phy, cmd); +- return rc; ++ phy_ethtool_ksettings_get(phy, cmd); ++ return 0; + } + + static int +@@ -443,6 +444,9 @@ static void stmmac_ethtool_gregs(struct + + priv->hw->mac->dump_regs(priv->hw, reg_space); + priv->hw->dma->dump_regs(priv->ioaddr, reg_space); ++ /* Copy DMA registers to where ethtool expects them */ ++ memcpy(®_space[ETHTOOL_DMA_OFFSET], ®_space[DMA_BUS_MODE / 4], ++ NUM_DWMAC1000_DMA_REGS * 4); + } + + static void +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -235,6 +235,17 @@ static void stmmac_clk_csr_set(struct st + else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M)) + priv->clk_csr = STMMAC_CSR_250_300M; + } ++ ++ if (priv->plat->has_sun8i) { ++ if (clk_rate > 160000000) ++ priv->clk_csr = 0x03; ++ else if (clk_rate > 80000000) ++ priv->clk_csr = 0x02; ++ else if (clk_rate > 40000000) ++ priv->clk_csr = 0x01; ++ else ++ priv->clk_csr = 0; ++ } + } + + static void print_pkt(unsigned char *buf, int len) +@@ -783,7 +794,7 @@ static void stmmac_adjust_link(struct ne + struct stmmac_priv *priv = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; + unsigned long flags; +- int new_state = 0; ++ bool new_state = false; + + if (!phydev) + return; +@@ -796,8 +807,8 @@ static void stmmac_adjust_link(struct ne + /* Now we make sure that we can be in full duplex mode. + * If not, we operate in half-duplex mode. */ + if (phydev->duplex != priv->oldduplex) { +- new_state = 1; +- if (!(phydev->duplex)) ++ new_state = true; ++ if (!phydev->duplex) + ctrl &= ~priv->hw->link.duplex; + else + ctrl |= priv->hw->link.duplex; +@@ -808,30 +819,17 @@ static void stmmac_adjust_link(struct ne + stmmac_mac_flow_ctrl(priv, phydev->duplex); + + if (phydev->speed != priv->speed) { +- new_state = 1; ++ new_state = true; ++ ctrl &= ~priv->hw->link.speed_mask; + switch (phydev->speed) { +- case 1000: +- if (priv->plat->has_gmac || +- priv->plat->has_gmac4) +- ctrl &= ~priv->hw->link.port; ++ case SPEED_1000: ++ ctrl |= priv->hw->link.speed1000; + break; +- case 100: +- if (priv->plat->has_gmac || +- priv->plat->has_gmac4) { +- ctrl |= priv->hw->link.port; +- ctrl |= priv->hw->link.speed; +- } else { +- ctrl &= ~priv->hw->link.port; +- } ++ case SPEED_100: ++ ctrl |= priv->hw->link.speed100; + break; +- case 10: +- if (priv->plat->has_gmac || +- priv->plat->has_gmac4) { +- ctrl |= priv->hw->link.port; +- ctrl &= ~(priv->hw->link.speed); +- } else { +- ctrl &= ~priv->hw->link.port; +- } ++ case SPEED_10: ++ ctrl |= priv->hw->link.speed10; + break; + default: + netif_warn(priv, link, priv->dev, +@@ -847,12 +845,12 @@ static void stmmac_adjust_link(struct ne + writel(ctrl, priv->ioaddr + MAC_CTRL_REG); + + if (!priv->oldlink) { +- new_state = 1; +- priv->oldlink = 1; ++ new_state = true; ++ priv->oldlink = true; + } + } else if (priv->oldlink) { +- new_state = 1; +- priv->oldlink = 0; ++ new_state = true; ++ priv->oldlink = false; + priv->speed = SPEED_UNKNOWN; + priv->oldduplex = DUPLEX_UNKNOWN; + } +@@ -915,7 +913,7 @@ static int stmmac_init_phy(struct net_de + char bus_id[MII_BUS_ID_SIZE]; + int interface = priv->plat->interface; + int max_speed = priv->plat->max_speed; +- priv->oldlink = 0; ++ priv->oldlink = false; + priv->speed = SPEED_UNKNOWN; + priv->oldduplex = DUPLEX_UNKNOWN; + +@@ -1450,7 +1448,7 @@ static void free_dma_rx_desc_resources(s + static void free_dma_tx_desc_resources(struct stmmac_priv *priv) + { + u32 tx_count = priv->plat->tx_queues_to_use; +- u32 queue = 0; ++ u32 queue; + + /* Free TX queue resources */ + for (queue = 0; queue < tx_count; queue++) { +@@ -1499,7 +1497,7 @@ static int alloc_dma_rx_desc_resources(s + sizeof(dma_addr_t), + GFP_KERNEL); + if (!rx_q->rx_skbuff_dma) +- return -ENOMEM; ++ goto err_dma; + + rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE, + sizeof(struct sk_buff *), +@@ -1562,13 +1560,13 @@ static int alloc_dma_tx_desc_resources(s + sizeof(*tx_q->tx_skbuff_dma), + GFP_KERNEL); + if (!tx_q->tx_skbuff_dma) +- return -ENOMEM; ++ goto err_dma; + + tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!tx_q->tx_skbuff) +- goto err_dma_buffers; ++ goto err_dma; + + if (priv->extend_desc) { + tx_q->dma_etx = dma_zalloc_coherent(priv->device, +@@ -1578,7 +1576,7 @@ static int alloc_dma_tx_desc_resources(s + &tx_q->dma_tx_phy, + GFP_KERNEL); + if (!tx_q->dma_etx) +- goto err_dma_buffers; ++ goto err_dma; + } else { + tx_q->dma_tx = dma_zalloc_coherent(priv->device, + DMA_TX_SIZE * +@@ -1587,13 +1585,13 @@ static int alloc_dma_tx_desc_resources(s + &tx_q->dma_tx_phy, + GFP_KERNEL); + if (!tx_q->dma_tx) +- goto err_dma_buffers; ++ goto err_dma; + } + } + + return 0; + +-err_dma_buffers: ++err_dma: + free_dma_tx_desc_resources(priv); + + return ret; +@@ -2895,8 +2893,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + priv->xstats.tx_set_ic_bit++; + } + +- if (!priv->hwts_tx_en) +- skb_tx_timestamp(skb); ++ skb_tx_timestamp(skb); + + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + priv->hwts_tx_en)) { +@@ -2974,7 +2971,7 @@ static netdev_tx_t stmmac_xmit(struct sk + + /* Manage oversized TCP frames for GMAC4 device */ + if (skb_is_gso(skb) && priv->tso) { +- if (ip_hdr(skb)->protocol == IPPROTO_TCP) ++ if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) + return stmmac_tso_xmit(skb, dev); + } + +@@ -3105,8 +3102,7 @@ static netdev_tx_t stmmac_xmit(struct sk + priv->xstats.tx_set_ic_bit++; + } + +- if (!priv->hwts_tx_en) +- skb_tx_timestamp(skb); ++ skb_tx_timestamp(skb); + + /* Ready to fill the first descriptor and set the OWN bit w/o any + * problems because all the descriptors are actually ready to be +@@ -3983,7 +3979,9 @@ static int stmmac_hw_init(struct stmmac_ + struct mac_device_info *mac; + + /* Identify the MAC HW device */ +- if (priv->plat->has_gmac) { ++ if (priv->plat->setup) { ++ mac = priv->plat->setup(priv); ++ } else if (priv->plat->has_gmac) { + priv->dev->priv_flags |= IFF_UNICAST_FLT; + mac = dwmac1000_setup(priv->ioaddr, + priv->plat->multicast_filter_bins, +@@ -4003,6 +4001,10 @@ static int stmmac_hw_init(struct stmmac_ + + priv->hw = mac; + ++ /* dwmac-sun8i only work in chain mode */ ++ if (priv->plat->has_sun8i) ++ chain_mode = 1; ++ + /* To use the chained or ring mode */ + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + priv->hw->mode = &dwmac4_ring_mode_ops; +@@ -4131,8 +4133,15 @@ int stmmac_dvr_probe(struct device *devi + if ((phyaddr >= 0) && (phyaddr <= 31)) + priv->plat->phy_addr = phyaddr; + +- if (priv->plat->stmmac_rst) ++ if (priv->plat->stmmac_rst) { ++ ret = reset_control_assert(priv->plat->stmmac_rst); + reset_control_deassert(priv->plat->stmmac_rst); ++ /* Some reset controllers have only reset callback instead of ++ * assert + deassert callbacks pair. ++ */ ++ if (ret == -ENOTSUPP) ++ reset_control_reset(priv->plat->stmmac_rst); ++ } + + /* Init MAC and get the capabilities */ + ret = stmmac_hw_init(priv); +@@ -4149,7 +4158,7 @@ int stmmac_dvr_probe(struct device *devi + NETIF_F_RXCSUM; + + if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) { +- ndev->hw_features |= NETIF_F_TSO; ++ ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; + priv->tso = true; + dev_info(priv->device, "TSO feature enabled\n"); + } +@@ -4311,7 +4320,7 @@ int stmmac_suspend(struct device *dev) + } + spin_unlock_irqrestore(&priv->lock, flags); + +- priv->oldlink = 0; ++ priv->oldlink = false; + priv->speed = SPEED_UNKNOWN; + priv->oldduplex = DUPLEX_UNKNOWN; + return 0; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +@@ -204,6 +204,7 @@ int stmmac_mdio_register(struct net_devi + struct stmmac_priv *priv = netdev_priv(ndev); + struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; + struct device_node *mdio_node = priv->plat->mdio_node; ++ struct device *dev = ndev->dev.parent; + int addr, found; + + if (!mdio_bus_data) +@@ -237,7 +238,7 @@ int stmmac_mdio_register(struct net_devi + else + err = mdiobus_register(new_bus); + if (err != 0) { +- netdev_err(ndev, "Cannot register the MDIO bus\n"); ++ dev_err(dev, "Cannot register the MDIO bus\n"); + goto bus_register_fail; + } + +@@ -292,7 +293,7 @@ int stmmac_mdio_register(struct net_devi + } + + if (!found && !mdio_node) { +- netdev_warn(ndev, "No PHY found\n"); ++ dev_warn(dev, "No PHY found\n"); + mdiobus_unregister(new_bus); + mdiobus_free(new_bus); + return -ENODEV; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +@@ -30,42 +30,39 @@ + * negative value of the address means that MAC controller is not connected + * with PHY. + */ +-struct stmmac_pci_dmi_data { +- const char *name; +- const char *asset_tag; ++struct stmmac_pci_func_data { + unsigned int func; + int phy_addr; + }; + +-struct stmmac_pci_info { +- struct pci_dev *pdev; +- int (*setup)(struct plat_stmmacenet_data *plat, +- struct stmmac_pci_info *info); +- struct stmmac_pci_dmi_data *dmi; ++struct stmmac_pci_dmi_data { ++ const struct stmmac_pci_func_data *func; ++ size_t nfuncs; + }; + +-static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info) +-{ +- const char *name = dmi_get_system_info(DMI_BOARD_NAME); +- const char *asset_tag = dmi_get_system_info(DMI_BOARD_ASSET_TAG); +- unsigned int func = PCI_FUNC(info->pdev->devfn); +- struct stmmac_pci_dmi_data *dmi; +- +- /* +- * Galileo boards with old firmware don't support DMI. We always return +- * 1 here, so at least first found MAC controller would be probed. +- */ +- if (!name) +- return 1; ++struct stmmac_pci_info { ++ int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat); ++}; + +- for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) { +- if (!strcmp(dmi->name, name) && dmi->func == func) { +- /* If asset tag is provided, match on it as well. */ +- if (dmi->asset_tag && strcmp(dmi->asset_tag, asset_tag)) +- continue; +- return dmi->phy_addr; +- } +- } ++static int stmmac_pci_find_phy_addr(struct pci_dev *pdev, ++ const struct dmi_system_id *dmi_list) ++{ ++ const struct stmmac_pci_func_data *func_data; ++ const struct stmmac_pci_dmi_data *dmi_data; ++ const struct dmi_system_id *dmi_id; ++ int func = PCI_FUNC(pdev->devfn); ++ size_t n; ++ ++ dmi_id = dmi_first_match(dmi_list); ++ if (!dmi_id) ++ return -ENODEV; ++ ++ dmi_data = dmi_id->driver_data; ++ func_data = dmi_data->func; ++ ++ for (n = 0; n < dmi_data->nfuncs; n++, func_data++) ++ if (func_data->func == func) ++ return func_data->phy_addr; + + return -ENODEV; + } +@@ -100,7 +97,8 @@ static void common_default_data(struct p + plat->rx_queues_cfg[0].pkt_route = 0x0; + } + +-static void stmmac_default_data(struct plat_stmmacenet_data *plat) ++static int stmmac_default_data(struct pci_dev *pdev, ++ struct plat_stmmacenet_data *plat) + { + /* Set common default data first */ + common_default_data(plat); +@@ -112,12 +110,77 @@ static void stmmac_default_data(struct p + plat->dma_cfg->pbl = 32; + plat->dma_cfg->pblx8 = true; + /* TODO: AXI */ ++ ++ return 0; + } + +-static int quark_default_data(struct plat_stmmacenet_data *plat, +- struct stmmac_pci_info *info) ++static const struct stmmac_pci_info stmmac_pci_info = { ++ .setup = stmmac_default_data, ++}; ++ ++static const struct stmmac_pci_func_data galileo_stmmac_func_data[] = { ++ { ++ .func = 6, ++ .phy_addr = 1, ++ }, ++}; ++ ++static const struct stmmac_pci_dmi_data galileo_stmmac_dmi_data = { ++ .func = galileo_stmmac_func_data, ++ .nfuncs = ARRAY_SIZE(galileo_stmmac_func_data), ++}; ++ ++static const struct stmmac_pci_func_data iot2040_stmmac_func_data[] = { ++ { ++ .func = 6, ++ .phy_addr = 1, ++ }, ++ { ++ .func = 7, ++ .phy_addr = 1, ++ }, ++}; ++ ++static const struct stmmac_pci_dmi_data iot2040_stmmac_dmi_data = { ++ .func = iot2040_stmmac_func_data, ++ .nfuncs = ARRAY_SIZE(iot2040_stmmac_func_data), ++}; ++ ++static const struct dmi_system_id quark_pci_dmi[] = { ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"), ++ }, ++ .driver_data = (void *)&galileo_stmmac_dmi_data, ++ }, ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"), ++ }, ++ .driver_data = (void *)&galileo_stmmac_dmi_data, ++ }, ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), ++ DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, ++ "6ES7647-0AA00-0YA2"), ++ }, ++ .driver_data = (void *)&galileo_stmmac_dmi_data, ++ }, ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), ++ DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, ++ "6ES7647-0AA00-1YA2"), ++ }, ++ .driver_data = (void *)&iot2040_stmmac_dmi_data, ++ }, ++ {} ++}; ++ ++static int quark_default_data(struct pci_dev *pdev, ++ struct plat_stmmacenet_data *plat) + { +- struct pci_dev *pdev = info->pdev; + int ret; + + /* Set common default data first */ +@@ -127,9 +190,19 @@ static int quark_default_data(struct pla + * Refuse to load the driver and register net device if MAC controller + * does not connect to any PHY interface. + */ +- ret = stmmac_pci_find_phy_addr(info); +- if (ret < 0) +- return ret; ++ ret = stmmac_pci_find_phy_addr(pdev, quark_pci_dmi); ++ if (ret < 0) { ++ /* Return error to the caller on DMI enabled boards. */ ++ if (dmi_get_system_info(DMI_BOARD_NAME)) ++ return ret; ++ ++ /* ++ * Galileo boards with old firmware don't support DMI. We always ++ * use 1 here as PHY address, so at least the first found MAC ++ * controller would be probed. ++ */ ++ ret = 1; ++ } + + plat->bus_id = PCI_DEVID(pdev->bus->number, pdev->devfn); + plat->phy_addr = ret; +@@ -143,41 +216,8 @@ static int quark_default_data(struct pla + return 0; + } + +-static struct stmmac_pci_dmi_data quark_pci_dmi_data[] = { +- { +- .name = "Galileo", +- .func = 6, +- .phy_addr = 1, +- }, +- { +- .name = "GalileoGen2", +- .func = 6, +- .phy_addr = 1, +- }, +- { +- .name = "SIMATIC IOT2000", +- .asset_tag = "6ES7647-0AA00-0YA2", +- .func = 6, +- .phy_addr = 1, +- }, +- { +- .name = "SIMATIC IOT2000", +- .asset_tag = "6ES7647-0AA00-1YA2", +- .func = 6, +- .phy_addr = 1, +- }, +- { +- .name = "SIMATIC IOT2000", +- .asset_tag = "6ES7647-0AA00-1YA2", +- .func = 7, +- .phy_addr = 1, +- }, +- {} +-}; +- +-static struct stmmac_pci_info quark_pci_info = { ++static const struct stmmac_pci_info quark_pci_info = { + .setup = quark_default_data, +- .dmi = quark_pci_dmi_data, + }; + + /** +@@ -236,15 +276,9 @@ static int stmmac_pci_probe(struct pci_d + + pci_set_master(pdev); + +- if (info) { +- info->pdev = pdev; +- if (info->setup) { +- ret = info->setup(plat, info); +- if (ret) +- return ret; +- } +- } else +- stmmac_default_data(plat); ++ ret = info->setup(pdev, plat); ++ if (ret) ++ return ret; + + pci_enable_msi(pdev); + +@@ -270,14 +304,21 @@ static void stmmac_pci_remove(struct pci + + static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_suspend, stmmac_resume); + +-#define STMMAC_VENDOR_ID 0x700 ++/* synthetic ID, no official vendor */ ++#define PCI_VENDOR_ID_STMMAC 0x700 ++ + #define STMMAC_QUARK_ID 0x0937 + #define STMMAC_DEVICE_ID 0x1108 + ++#define STMMAC_DEVICE(vendor_id, dev_id, info) { \ ++ PCI_VDEVICE(vendor_id, dev_id), \ ++ .driver_data = (kernel_ulong_t)&info \ ++ } ++ + static const struct pci_device_id stmmac_id_table[] = { +- {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)}, +- {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)}, +- {PCI_VDEVICE(INTEL, STMMAC_QUARK_ID), (kernel_ulong_t)&quark_pci_info}, ++ STMMAC_DEVICE(STMMAC, STMMAC_DEVICE_ID, stmmac_pci_info), ++ STMMAC_DEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_MAC, stmmac_pci_info), ++ STMMAC_DEVICE(INTEL, STMMAC_QUARK_ID, quark_pci_info), + {} + }; + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -309,6 +309,13 @@ static int stmmac_dt_phy(struct plat_stm + struct device_node *np, struct device *dev) + { + bool mdio = true; ++ static const struct of_device_id need_mdio_ids[] = { ++ { .compatible = "snps,dwc-qos-ethernet-4.10" }, ++ { .compatible = "allwinner,sun8i-a83t-emac" }, ++ { .compatible = "allwinner,sun8i-h3-emac" }, ++ { .compatible = "allwinner,sun8i-v3s-emac" }, ++ { .compatible = "allwinner,sun50i-a64-emac" }, ++ }; + + /* If phy-handle property is passed from DT, use it as the PHY */ + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); +@@ -325,8 +332,7 @@ static int stmmac_dt_phy(struct plat_stm + mdio = false; + } + +- /* exception for dwmac-dwc-qos-eth glue logic */ +- if (of_device_is_compatible(np, "snps,dwc-qos-ethernet-4.10")) { ++ if (of_match_node(need_mdio_ids, np)) { + plat->mdio_node = of_get_child_by_name(np, "mdio"); + } else { + /** +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -177,6 +177,7 @@ struct plat_stmmacenet_data { + void (*fix_mac_speed)(void *priv, unsigned int speed); + int (*init)(struct platform_device *pdev, void *priv); + void (*exit)(struct platform_device *pdev, void *priv); ++ struct mac_device_info *(*setup)(void *priv); + void *bsp_priv; + struct clk *stmmac_clk; + struct clk *pclk; +@@ -185,6 +186,7 @@ struct plat_stmmacenet_data { + struct reset_control *stmmac_rst; + struct stmmac_axi *axi; + int has_gmac4; ++ bool has_sun8i; + bool tso_en; + int mac_port_sel_speed; + bool en_tx_lpi_clockgating;