diff options
Diffstat (limited to 'target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch')
-rw-r--r-- | target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch | 4258 |
1 files changed, 4258 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch b/target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch new file mode 100644 index 0000000..539d1a1 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch @@ -0,0 +1,4258 @@ +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -99,6 +99,7 @@ struct plat_stmmacenet_data { + int phy_addr; + int interface; + struct stmmac_mdio_bus_data *mdio_bus_data; ++ struct device_node *phy_node; + struct stmmac_dma_cfg *dma_cfg; + int clk_csr; + int has_gmac; +@@ -114,32 +115,12 @@ struct plat_stmmacenet_data { + int maxmtu; + int multicast_filter_bins; + int unicast_filter_entries; ++ int tx_fifo_size; ++ int rx_fifo_size; + void (*fix_mac_speed)(void *priv, unsigned int speed); + void (*bus_setup)(void __iomem *ioaddr); +- void *(*setup)(struct platform_device *pdev); +- void (*free)(struct platform_device *pdev, void *priv); + int (*init)(struct platform_device *pdev, void *priv); + void (*exit)(struct platform_device *pdev, void *priv); +- void *custom_cfg; +- void *custom_data; + void *bsp_priv; + }; +- +-/* of_data for SoC glue layer device tree bindings */ +- +-struct stmmac_of_data { +- int has_gmac; +- int enh_desc; +- int tx_coe; +- int rx_coe; +- int bugged_jumbo; +- int pmt; +- int riwt_off; +- void (*fix_mac_speed)(void *priv, unsigned int speed); +- void (*bus_setup)(void __iomem *ioaddr); +- void *(*setup)(struct platform_device *pdev); +- void (*free)(struct platform_device *pdev, void *priv); +- int (*init)(struct platform_device *pdev, void *priv); +- void (*exit)(struct platform_device *pdev, void *priv); +-}; + #endif +--- a/drivers/net/ethernet/stmicro/Kconfig ++++ b/drivers/net/ethernet/stmicro/Kconfig +@@ -7,9 +7,7 @@ config NET_VENDOR_STMICRO + default y + depends on HAS_IOMEM + ---help--- +- If you have a network (Ethernet) card belonging to this class, say Y +- and read the Ethernet-HOWTO, available from +- <http://www.tldp.org/docs.html#howto>. ++ If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -14,21 +14,54 @@ config STMMAC_ETH + if STMMAC_ETH + + config STMMAC_PLATFORM +- bool "STMMAC Platform bus support" ++ tristate "STMMAC Platform bus support" + depends on STMMAC_ETH ++ select MFD_SYSCON + default y + ---help--- +- This selects the platform specific bus support for +- the stmmac device driver. This is the driver used +- on many embedded STM platforms based on ARM and SuperH +- processors. ++ This selects the platform specific bus support for the stmmac driver. ++ This is the driver used on several SoCs: ++ STi, Allwinner, Amlogic Meson, Altera SOCFPGA. ++ + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + ++if STMMAC_PLATFORM ++ ++config DWMAC_GENERIC ++ tristate "Generic driver for DWMAC" ++ default STMMAC_PLATFORM ++ ---help--- ++ Generic DWMAC driver for platforms that don't require any ++ platform specific code to function or is using platform ++ data for setup. ++ ++config DWMAC_IPQ806X ++ tristate "QCA IPQ806x DWMAC support" ++ default ARCH_QCOM ++ depends on OF ++ select MFD_SYSCON ++ help ++ Support for QCA IPQ806X DWMAC Ethernet. ++ ++ This selects the IPQ806x SoC glue layer support for the stmmac ++ device driver. This driver does not use any of the hardware ++ acceleration features available on this SoC. Network devices ++ will behave like standard non-accelerated ethernet interfaces. ++ ++config DWMAC_LPC18XX ++ tristate "NXP LPC18xx/43xx DWMAC support" ++ default ARCH_LPC18XX ++ depends on OF ++ select MFD_SYSCON ++ ---help--- ++ Support for NXP LPC18xx/43xx DWMAC Ethernet. ++ + config DWMAC_MESON +- bool "Amlogic Meson dwmac support" +- depends on STMMAC_PLATFORM && ARCH_MESON ++ tristate "Amlogic Meson dwmac support" ++ default ARCH_MESON ++ depends on OF + help + Support for Ethernet controller on Amlogic Meson SoCs. + +@@ -36,9 +69,22 @@ config DWMAC_MESON + the stmmac device driver. This driver is used for Meson6 and + Meson8 SoCs. + ++config DWMAC_ROCKCHIP ++ tristate "Rockchip dwmac support" ++ default ARCH_ROCKCHIP ++ depends on OF ++ select MFD_SYSCON ++ help ++ Support for Ethernet controller on Rockchip RK3288 SoC. ++ ++ This selects the Rockchip RK3288 SoC glue layer support for ++ the stmmac device driver. ++ + config DWMAC_SOCFPGA +- bool "SOCFPGA dwmac support" +- depends on STMMAC_PLATFORM && MFD_SYSCON && (ARCH_SOCFPGA || COMPILE_TEST) ++ tristate "SOCFPGA dwmac support" ++ default ARCH_SOCFPGA ++ depends on OF ++ select MFD_SYSCON + help + Support for ethernet controller on Altera SOCFPGA + +@@ -46,21 +92,11 @@ config DWMAC_SOCFPGA + for the stmmac device driver. This driver is used for + arria5 and cyclone5 FPGA SoCs. + +-config DWMAC_SUNXI +- bool "Allwinner GMAC support" +- depends on STMMAC_PLATFORM && ARCH_SUNXI +- default y +- ---help--- +- Support for Allwinner A20/A31 GMAC ethernet controllers. +- +- This selects Allwinner SoC glue layer support for the +- stmmac device driver. This driver is used for A20/A31 +- GMAC ethernet controller. +- + config DWMAC_STI +- bool "STi GMAC support" +- depends on STMMAC_PLATFORM && ARCH_STI +- default y ++ tristate "STi GMAC support" ++ default ARCH_STI ++ depends on OF ++ select MFD_SYSCON + ---help--- + Support for ethernet controller on STi SOCs. + +@@ -68,8 +104,20 @@ config DWMAC_STI + device driver. This driver is used on for the STi series + SOCs GMAC ethernet controller. + ++config DWMAC_SUNXI ++ tristate "Allwinner GMAC support" ++ default ARCH_SUNXI ++ depends on OF ++ ---help--- ++ Support for Allwinner A20/A31 GMAC ethernet controllers. ++ ++ This selects Allwinner SoC glue layer support for the ++ stmmac device driver. This driver is used for A20/A31 ++ GMAC ethernet controller. ++endif ++ + config STMMAC_PCI +- bool "STMMAC PCI bus support" ++ tristate "STMMAC PCI bus support" + depends on STMMAC_ETH && PCI + ---help--- + This is to select the Synopsys DWMAC available on PCI devices, +@@ -79,22 +127,4 @@ config STMMAC_PCI + D1215994A VIRTEX FPGA board. + + If unsure, say N. +- +-config STMMAC_DEBUG_FS +- bool "Enable monitoring via sysFS " +- default n +- depends on STMMAC_ETH && DEBUG_FS +- ---help--- +- The stmmac entry in /sys reports DMA TX/RX rings +- or (if supported) the HW cap register. +- +-config STMMAC_DA +- bool "STMMAC DMA arbitration scheme" +- default n +- ---help--- +- Selecting this option, rx has priority over Tx (only for Giga +- Ethernet device). +- By default, the DMA arbitration scheme is based on Round-robin +- (rx:tx priority is 1:1). +- + endif +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -1,11 +1,20 @@ + obj-$(CONFIG_STMMAC_ETH) += stmmac.o +-stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o +-stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o +-stmmac-$(CONFIG_DWMAC_MESON) += dwmac-meson.o +-stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o +-stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o +-stmmac-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o + stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ +- chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ +- dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ ++ chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ ++ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ + mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y) ++ ++# Ordering matters. Generic driver must be last. ++obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o ++obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o ++obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o ++obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o ++obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o ++obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o ++obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o ++obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o ++obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o ++stmmac-platform-objs:= stmmac_platform.o ++ ++obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o ++stmmac-pci-objs:= stmmac_pci.o +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -44,6 +44,7 @@ + #undef FRAME_FILTER_DEBUG + /* #define FRAME_FILTER_DEBUG */ + ++/* Extra statistic and debug information exposed by ethtool */ + struct stmmac_extra_stats { + /* Transmit errors */ + unsigned long tx_underflow ____cacheline_aligned; +@@ -149,7 +150,7 @@ struct stmmac_extra_stats { + #define MAC_CSR_H_FRQ_MASK 0x20 + + #define HASH_TABLE_SIZE 64 +-#define PAUSE_TIME 0x200 ++#define PAUSE_TIME 0xffff + + /* Flow Control defines */ + #define FLOW_OFF 0 +@@ -220,6 +221,7 @@ enum dma_irq_status { + handle_tx = 0x8, + }; + ++/* EEE and LPI defines */ + #define CORE_IRQ_TX_PATH_IN_LPI_MODE (1 << 0) + #define CORE_IRQ_TX_PATH_EXIT_LPI_MODE (1 << 1) + #define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 2) +@@ -229,6 +231,7 @@ enum dma_irq_status { + #define CORE_PCS_LINK_STATUS (1 << 6) + #define CORE_RGMII_IRQ (1 << 7) + ++/* Physical Coding Sublayer */ + struct rgmii_adv { + unsigned int pause; + unsigned int duplex; +@@ -294,6 +297,7 @@ struct dma_features { + + #define JUMBO_LEN 9000 + ++/* Descriptors helpers */ + struct stmmac_desc_ops { + /* DMA RX descriptor ring initialization */ + void (*init_rx_desc) (struct dma_desc *p, int disable_rx_ic, int mode, +@@ -341,6 +345,10 @@ struct stmmac_desc_ops { + int (*get_rx_timestamp_status) (void *desc, u32 ats); + }; + ++extern const struct stmmac_desc_ops enh_desc_ops; ++extern const struct stmmac_desc_ops ndesc_ops; ++ ++/* Specific DMA helpers */ + struct stmmac_dma_ops { + /* DMA core initialization */ + int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, +@@ -349,7 +357,8 @@ struct stmmac_dma_ops { + void (*dump_regs) (void __iomem *ioaddr); + /* Set tx/rx threshold in the csr6 register + * An invalid value enables the store-and-forward mode */ +- void (*dma_mode) (void __iomem *ioaddr, int txmode, int rxmode); ++ void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode, ++ int rxfifosz); + /* To track extra statistic (if supported) */ + void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x, + void __iomem *ioaddr); +@@ -370,6 +379,7 @@ struct stmmac_dma_ops { + + struct mac_device_info; + ++/* Helpers to program the MAC core */ + struct stmmac_ops { + /* MAC core initialization */ + void (*core_init)(struct mac_device_info *hw, int mtu); +@@ -400,6 +410,7 @@ struct stmmac_ops { + void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv); + }; + ++/* PTP and HW Timer helpers */ + struct stmmac_hwtimestamp { + void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data); + void (*config_sub_second_increment) (void __iomem *ioaddr); +@@ -410,6 +421,8 @@ struct stmmac_hwtimestamp { + u64(*get_systime) (void __iomem *ioaddr); + }; + ++extern const struct stmmac_hwtimestamp stmmac_ptp; ++ + struct mac_link { + int port; + int duplex; +@@ -421,6 +434,7 @@ struct mii_regs { + unsigned int data; /* MII Data */ + }; + ++/* Helpers to manage the descriptors for chain and ring modes */ + struct stmmac_mode_ops { + void (*init) (void *des, dma_addr_t phy_addr, unsigned int size, + unsigned int extend_desc); +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c +@@ -0,0 +1,81 @@ ++/* ++ * Generic DWMAC platform driver ++ * ++ * Copyright (C) 2007-2011 STMicroelectronics Ltd ++ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++ ++#include "stmmac.h" ++#include "stmmac_platform.h" ++ ++static int dwmac_generic_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ int ret; ++ ++ ret = stmmac_get_platform_resources(pdev, &stmmac_res); ++ if (ret) ++ return ret; ++ ++ if (pdev->dev.of_node) { ++ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); ++ if (IS_ERR(plat_dat)) { ++ dev_err(&pdev->dev, "dt configuration failed\n"); ++ return PTR_ERR(plat_dat); ++ } ++ } else { ++ plat_dat = dev_get_platdata(&pdev->dev); ++ if (!plat_dat) { ++ dev_err(&pdev->dev, "no platform data provided\n"); ++ return -EINVAL; ++ } ++ ++ /* Set default value for multicast hash bins */ ++ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; ++ ++ /* Set default value for unicast filter entries */ ++ plat_dat->unicast_filter_entries = 1; ++ } ++ ++ /* Custom initialisation (if needed) */ ++ if (plat_dat->init) { ++ ret = plat_dat->init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; ++ } ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++} ++ ++static const struct of_device_id dwmac_generic_match[] = { ++ { .compatible = "st,spear600-gmac"}, ++ { .compatible = "snps,dwmac-3.610"}, ++ { .compatible = "snps,dwmac-3.70a"}, ++ { .compatible = "snps,dwmac-3.710"}, ++ { .compatible = "snps,dwmac"}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, dwmac_generic_match); ++ ++static struct platform_driver dwmac_generic_driver = { ++ .probe = dwmac_generic_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = STMMAC_RESOURCE_NAME, ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = of_match_ptr(dwmac_generic_match), ++ }, ++}; ++module_platform_driver(dwmac_generic_driver); ++ ++MODULE_DESCRIPTION("Generic dwmac driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +@@ -0,0 +1,373 @@ ++/* ++ * Qualcomm Atheros IPQ806x GMAC glue layer ++ * ++ * Copyright (C) 2015 The Linux Foundation ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/phy.h> ++#include <linux/regmap.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++#include <linux/of_net.h> ++#include <linux/mfd/syscon.h> ++#include <linux/stmmac.h> ++#include <linux/of_mdio.h> ++#include <linux/module.h> ++ ++#include "stmmac_platform.h" ++ ++#define NSS_COMMON_CLK_GATE 0x8 ++#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x) ++#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2)) ++#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2)) ++#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x) ++#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x) ++ ++#define NSS_COMMON_CLK_DIV0 0xC ++#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8) ++#define NSS_COMMON_CLK_DIV_MASK 0x7f ++ ++#define NSS_COMMON_CLK_SRC_CTRL 0x14 ++#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (x) ++/* Mode is coded on 1 bit but is different depending on the MAC ID: ++ * MAC0: QSGMII=0 RGMII=1 ++ * MAC1: QSGMII=0 SGMII=0 RGMII=1 ++ * MAC2 & MAC3: QSGMII=0 SGMII=1 ++ */ ++#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1 ++#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0) ++ ++#define NSS_COMMON_MACSEC_CTL 0x28 ++#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x) ++ ++#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4)) ++#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19) ++#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16) ++#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8 ++#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0 ++#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f ++ ++#define NSS_COMMON_CLK_DIV_RGMII_1000 1 ++#define NSS_COMMON_CLK_DIV_RGMII_100 9 ++#define NSS_COMMON_CLK_DIV_RGMII_10 99 ++#define NSS_COMMON_CLK_DIV_SGMII_1000 0 ++#define NSS_COMMON_CLK_DIV_SGMII_100 4 ++#define NSS_COMMON_CLK_DIV_SGMII_10 49 ++ ++#define QSGMII_PCS_MODE_CTL 0x68 ++#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7) ++ ++#define QSGMII_PCS_CAL_LCKDT_CTL 0x120 ++#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) ++ ++/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */ ++#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \ ++ (0x13c + (4 * (x - 2)))) ++#define QSGMII_PHY_CDR_EN BIT(0) ++#define QSGMII_PHY_RX_FRONT_EN BIT(1) ++#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2) ++#define QSGMII_PHY_TX_DRIVER_EN BIT(3) ++#define QSGMII_PHY_QSGMII_EN BIT(7) ++#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12 ++#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7 ++#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18 ++#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3 ++#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20 ++#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3 ++#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22 ++#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3 ++#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28 ++#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf ++ ++struct ipq806x_gmac { ++ struct platform_device *pdev; ++ struct regmap *nss_common; ++ struct regmap *qsgmii_csr; ++ uint32_t id; ++ struct clk *core_clk; ++ phy_interface_t phy_mode; ++}; ++ ++static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed) ++{ ++ struct device *dev = &gmac->pdev->dev; ++ int div; ++ ++ switch (speed) { ++ case SPEED_1000: ++ div = NSS_COMMON_CLK_DIV_SGMII_1000; ++ break; ++ ++ case SPEED_100: ++ div = NSS_COMMON_CLK_DIV_SGMII_100; ++ break; ++ ++ case SPEED_10: ++ div = NSS_COMMON_CLK_DIV_SGMII_10; ++ break; ++ ++ default: ++ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed); ++ return -EINVAL; ++ } ++ ++ return div; ++} ++ ++static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed) ++{ ++ struct device *dev = &gmac->pdev->dev; ++ int div; ++ ++ switch (speed) { ++ case SPEED_1000: ++ div = NSS_COMMON_CLK_DIV_RGMII_1000; ++ break; ++ ++ case SPEED_100: ++ div = NSS_COMMON_CLK_DIV_RGMII_100; ++ break; ++ ++ case SPEED_10: ++ div = NSS_COMMON_CLK_DIV_RGMII_10; ++ break; ++ ++ default: ++ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed); ++ return -EINVAL; ++ } ++ ++ return div; ++} ++ ++static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed) ++{ ++ uint32_t clk_bits, val; ++ int div; ++ ++ switch (gmac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ div = get_clk_div_rgmii(gmac, speed); ++ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) | ++ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); ++ break; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ div = get_clk_div_sgmii(gmac, speed); ++ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) | ++ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); ++ break; ++ ++ default: ++ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n", ++ phy_modes(gmac->phy_mode)); ++ return -EINVAL; ++ } ++ ++ /* Disable the clocks */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); ++ val &= ~clk_bits; ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); ++ ++ /* Set the divider */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val); ++ val &= ~(NSS_COMMON_CLK_DIV_MASK ++ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id)); ++ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id); ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val); ++ ++ /* Enable the clock back */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); ++ val |= clk_bits; ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); ++ ++ return 0; ++} ++ ++static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) ++{ ++ struct device *dev = &gmac->pdev->dev; ++ ++ gmac->phy_mode = of_get_phy_mode(dev->of_node); ++ if (gmac->phy_mode < 0) { ++ dev_err(dev, "missing phy mode property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) { ++ dev_err(dev, "missing qcom id property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ /* The GMACs are called 1 to 4 in the documentation, but to simplify the ++ * code and keep it consistent with the Linux convention, we'll number ++ * them from 0 to 3 here. ++ */ ++ if (gmac->id < 0 || gmac->id > 3) { ++ dev_err(dev, "invalid gmac id\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ gmac->core_clk = devm_clk_get(dev, "stmmaceth"); ++ if (IS_ERR(gmac->core_clk)) { ++ dev_err(dev, "missing stmmaceth clk property\n"); ++ return gmac->core_clk; ++ } ++ clk_set_rate(gmac->core_clk, 266000000); ++ ++ /* Setup the register map for the nss common registers */ ++ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "qcom,nss-common"); ++ if (IS_ERR(gmac->nss_common)) { ++ dev_err(dev, "missing nss-common node\n"); ++ return gmac->nss_common; ++ } ++ ++ /* Setup the register map for the qsgmii csr registers */ ++ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "qcom,qsgmii-csr"); ++ if (IS_ERR(gmac->qsgmii_csr)) { ++ dev_err(dev, "missing qsgmii-csr node\n"); ++ return gmac->qsgmii_csr; ++ } ++ ++ return NULL; ++} ++ ++static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed) ++{ ++ struct ipq806x_gmac *gmac = priv; ++ ++ ipq806x_gmac_set_speed(gmac, speed); ++} ++ ++static int ipq806x_gmac_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ struct device *dev = &pdev->dev; ++ struct ipq806x_gmac *gmac; ++ int val; ++ void *err; ++ ++ val = stmmac_get_platform_resources(pdev, &stmmac_res); ++ if (val) ++ return val; ++ ++ 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->pdev = pdev; ++ ++ err = ipq806x_gmac_of_parse(gmac); ++ if (IS_ERR(err)) { ++ dev_err(dev, "device tree parsing error\n"); ++ return PTR_ERR(err); ++ } ++ ++ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL, ++ QSGMII_PCS_CAL_LCKDT_CTL_RST); ++ ++ /* Inter frame gap is set to 12 */ ++ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET | ++ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET; ++ /* We also initiate an AXI low power exit request */ ++ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ; ++ switch (gmac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; ++ break; ++ default: ++ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", ++ phy_modes(gmac->phy_mode)); ++ return -EINVAL; ++ } ++ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val); ++ ++ /* Configure the clock src according to the mode */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val); ++ val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id)); ++ switch (gmac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) << ++ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) << ++ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); ++ break; ++ default: ++ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", ++ phy_modes(gmac->phy_mode)); ++ return -EINVAL; ++ } ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val); ++ ++ /* Enable PTP clock */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); ++ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id); ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); ++ ++ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) { ++ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id), ++ QSGMII_PHY_CDR_EN | ++ QSGMII_PHY_RX_FRONT_EN | ++ QSGMII_PHY_RX_SIGNAL_DETECT_EN | ++ QSGMII_PHY_TX_DRIVER_EN | ++ QSGMII_PHY_QSGMII_EN | ++ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET | ++ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET | ++ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET | ++ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET | ++ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET); ++ } ++ ++ plat_dat->has_gmac = true; ++ plat_dat->bsp_priv = gmac; ++ plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++} ++ ++static const struct of_device_id ipq806x_gmac_dwmac_match[] = { ++ { .compatible = "qcom,ipq806x-gmac" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ipq806x_gmac_dwmac_match); ++ ++static struct platform_driver ipq806x_gmac_dwmac_driver = { ++ .probe = ipq806x_gmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "ipq806x-gmac-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = ipq806x_gmac_dwmac_match, ++ }, ++}; ++module_platform_driver(ipq806x_gmac_dwmac_driver); ++ ++MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>"); ++MODULE_DESCRIPTION("Qualcomm Atheros IPQ806x DWMAC specific glue layer"); ++MODULE_LICENSE("Dual BSD/GPL"); +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c +@@ -0,0 +1,86 @@ ++/* ++ * DWMAC glue for NXP LPC18xx/LPC43xx Ethernet ++ * ++ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_net.h> ++#include <linux/phy.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <linux/stmmac.h> ++ ++#include "stmmac_platform.h" ++ ++/* Register defines for CREG syscon */ ++#define LPC18XX_CREG_CREG6 0x12c ++# define LPC18XX_CREG_CREG6_ETHMODE_MASK 0x7 ++# define LPC18XX_CREG_CREG6_ETHMODE_MII 0x0 ++# define LPC18XX_CREG_CREG6_ETHMODE_RMII 0x4 ++ ++static int lpc18xx_dwmac_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ struct regmap *reg; ++ u8 ethmode; ++ 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); ++ ++ plat_dat->has_gmac = true; ++ ++ reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg"); ++ if (IS_ERR(reg)) { ++ dev_err(&pdev->dev, "syscon lookup failed\n"); ++ return PTR_ERR(reg); ++ } ++ ++ if (plat_dat->interface == PHY_INTERFACE_MODE_MII) { ++ ethmode = LPC18XX_CREG_CREG6_ETHMODE_MII; ++ } else if (plat_dat->interface == PHY_INTERFACE_MODE_RMII) { ++ ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII; ++ } else { ++ dev_err(&pdev->dev, "Only MII and RMII mode supported\n"); ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(reg, LPC18XX_CREG_CREG6, ++ LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode); ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++} ++ ++static const struct of_device_id lpc18xx_dwmac_match[] = { ++ { .compatible = "nxp,lpc1850-dwmac" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, lpc18xx_dwmac_match); ++ ++static struct platform_driver lpc18xx_dwmac_driver = { ++ .probe = lpc18xx_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "lpc18xx-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = lpc18xx_dwmac_match, ++ }, ++}; ++module_platform_driver(lpc18xx_dwmac_driver); ++ ++MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); ++MODULE_DESCRIPTION("DWMAC glue for LPC18xx/43xx Ethernet"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c +@@ -15,9 +15,12 @@ + #include <linux/ethtool.h> + #include <linux/io.h> + #include <linux/ioport.h> ++#include <linux/module.h> + #include <linux/platform_device.h> + #include <linux/stmmac.h> + ++#include "stmmac_platform.h" ++ + #define ETHMAC_SPEED_100 BIT(1) + + struct meson_dwmac { +@@ -44,24 +47,54 @@ static void meson6_dwmac_fix_mac_speed(v + writel(val, dwmac->reg); + } + +-static void *meson6_dwmac_setup(struct platform_device *pdev) ++static int meson6_dwmac_probe(struct platform_device *pdev) + { ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; + struct meson_dwmac *dwmac; + struct resource *res; ++ 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); + + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) +- return ERR_PTR(-ENOMEM); ++ return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dwmac->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dwmac->reg)) +- return dwmac->reg; ++ return PTR_ERR(dwmac->reg); ++ ++ plat_dat->bsp_priv = dwmac; ++ plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed; + +- return dwmac; ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + } + +-const struct stmmac_of_data meson6_dwmac_data = { +- .setup = meson6_dwmac_setup, +- .fix_mac_speed = meson6_dwmac_fix_mac_speed, ++static const struct of_device_id meson6_dwmac_match[] = { ++ { .compatible = "amlogic,meson6-dwmac" }, ++ { } + }; ++MODULE_DEVICE_TABLE(of, meson6_dwmac_match); ++ ++static struct platform_driver meson6_dwmac_driver = { ++ .probe = meson6_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "meson6-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = meson6_dwmac_match, ++ }, ++}; ++module_platform_driver(meson6_dwmac_driver); ++ ++MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); ++MODULE_DESCRIPTION("Amlogic Meson DWMAC glue layer"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +@@ -0,0 +1,626 @@ ++/** ++ * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer ++ * ++ * Copyright (C) 2014 Chen-Zhi (Roger Chen) ++ * ++ * Chen-Zhi (Roger Chen) <roger.chen@rock-chips.com> ++ * ++ * 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 <linux/stmmac.h> ++#include <linux/bitops.h> ++#include <linux/clk.h> ++#include <linux/phy.h> ++#include <linux/of_net.h> ++#include <linux/gpio.h> ++#include <linux/module.h> ++#include <linux/of_gpio.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/delay.h> ++#include <linux/mfd/syscon.h> ++#include <linux/regmap.h> ++ ++#include "stmmac_platform.h" ++ ++struct rk_priv_data; ++struct rk_gmac_ops { ++ void (*set_to_rgmii)(struct rk_priv_data *bsp_priv, ++ int tx_delay, int rx_delay); ++ void (*set_to_rmii)(struct rk_priv_data *bsp_priv); ++ void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed); ++ void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed); ++}; ++ ++struct rk_priv_data { ++ struct platform_device *pdev; ++ int phy_iface; ++ struct regulator *regulator; ++ const struct rk_gmac_ops *ops; ++ ++ bool clk_enabled; ++ bool clock_input; ++ ++ struct clk *clk_mac; ++ struct clk *gmac_clkin; ++ struct clk *mac_clk_rx; ++ struct clk *mac_clk_tx; ++ struct clk *clk_mac_ref; ++ struct clk *clk_mac_refout; ++ struct clk *aclk_mac; ++ struct clk *pclk_mac; ++ ++ int tx_delay; ++ int rx_delay; ++ ++ struct regmap *grf; ++}; ++ ++#define HIWORD_UPDATE(val, mask, shift) \ ++ ((val) << (shift) | (mask) << ((shift) + 16)) ++ ++#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16)) ++#define GRF_CLR_BIT(nr) (BIT(nr+16)) ++ ++#define RK3288_GRF_SOC_CON1 0x0248 ++#define RK3288_GRF_SOC_CON3 0x0250 ++ ++/*RK3288_GRF_SOC_CON1*/ ++#define RK3288_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | \ ++ GRF_CLR_BIT(8)) ++#define RK3288_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | \ ++ GRF_BIT(8)) ++#define RK3288_GMAC_FLOW_CTRL GRF_BIT(9) ++#define RK3288_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9) ++#define RK3288_GMAC_SPEED_10M GRF_CLR_BIT(10) ++#define RK3288_GMAC_SPEED_100M GRF_BIT(10) ++#define RK3288_GMAC_RMII_CLK_25M GRF_BIT(11) ++#define RK3288_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11) ++#define RK3288_GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) ++#define RK3288_GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13)) ++#define RK3288_GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13)) ++#define RK3288_GMAC_RMII_MODE GRF_BIT(14) ++#define RK3288_GMAC_RMII_MODE_CLR GRF_CLR_BIT(14) ++ ++/*RK3288_GRF_SOC_CON3*/ ++#define RK3288_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) ++#define RK3288_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) ++#define RK3288_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) ++#define RK3288_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) ++#define RK3288_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) ++#define RK3288_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) ++ ++static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv, ++ int tx_delay, int rx_delay) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "Missing rockchip,grf property\n"); ++ return; ++ } ++ ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_PHY_INTF_SEL_RGMII | ++ RK3288_GMAC_RMII_MODE_CLR); ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, ++ RK3288_GMAC_RXCLK_DLY_ENABLE | ++ RK3288_GMAC_TXCLK_DLY_ENABLE | ++ RK3288_GMAC_CLK_RX_DL_CFG(rx_delay) | ++ RK3288_GMAC_CLK_TX_DL_CFG(tx_delay)); ++} ++ ++static void rk3288_set_to_rmii(struct rk_priv_data *bsp_priv) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "Missing rockchip,grf property\n"); ++ return; ++ } ++ ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_PHY_INTF_SEL_RMII | RK3288_GMAC_RMII_MODE); ++} ++ ++static void rk3288_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "Missing rockchip,grf property\n"); ++ return; ++ } ++ ++ if (speed == 10) ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_CLK_2_5M); ++ else if (speed == 100) ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_CLK_25M); ++ else if (speed == 1000) ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_CLK_125M); ++ else ++ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); ++} ++ ++static void rk3288_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "Missing rockchip,grf property\n"); ++ return; ++ } ++ ++ if (speed == 10) { ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_RMII_CLK_2_5M | ++ RK3288_GMAC_SPEED_10M); ++ } else if (speed == 100) { ++ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, ++ RK3288_GMAC_RMII_CLK_25M | ++ RK3288_GMAC_SPEED_100M); ++ } else { ++ dev_err(dev, "unknown speed value for RMII! speed=%d", speed); ++ } ++} ++ ++static const struct rk_gmac_ops rk3288_ops = { ++ .set_to_rgmii = rk3288_set_to_rgmii, ++ .set_to_rmii = rk3288_set_to_rmii, ++ .set_rgmii_speed = rk3288_set_rgmii_speed, ++ .set_rmii_speed = rk3288_set_rmii_speed, ++}; ++ ++#define RK3368_GRF_SOC_CON15 0x043c ++#define RK3368_GRF_SOC_CON16 0x0440 ++ ++/* RK3368_GRF_SOC_CON15 */ ++#define RK3368_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \ ++ GRF_CLR_BIT(11)) ++#define RK3368_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \ ++ GRF_BIT(11)) ++#define RK3368_GMAC_FLOW_CTRL GRF_BIT(8) ++#define RK3368_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8) ++#define RK3368_GMAC_SPEED_10M GRF_CLR_BIT(7) ++#define RK3368_GMAC_SPEED_100M GRF_BIT(7) ++#define RK3368_GMAC_RMII_CLK_25M GRF_BIT(3) ++#define RK3368_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3) ++#define RK3368_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5)) ++#define RK3368_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5)) ++#define RK3368_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5)) ++#define RK3368_GMAC_RMII_MODE GRF_BIT(6) ++#define RK3368_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6) ++ ++/* RK3368_GRF_SOC_CON16 */ ++#define RK3368_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7) ++#define RK3368_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7) ++#define RK3368_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) ++#define RK3368_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) ++#define RK3368_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) ++#define RK3368_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) ++ ++static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv, ++ int tx_delay, int rx_delay) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); ++ return; ++ } ++ ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_PHY_INTF_SEL_RGMII | ++ RK3368_GMAC_RMII_MODE_CLR); ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16, ++ RK3368_GMAC_RXCLK_DLY_ENABLE | ++ RK3368_GMAC_TXCLK_DLY_ENABLE | ++ RK3368_GMAC_CLK_RX_DL_CFG(rx_delay) | ++ RK3368_GMAC_CLK_TX_DL_CFG(tx_delay)); ++} ++ ++static void rk3368_set_to_rmii(struct rk_priv_data *bsp_priv) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); ++ return; ++ } ++ ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_PHY_INTF_SEL_RMII | RK3368_GMAC_RMII_MODE); ++} ++ ++static void rk3368_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); ++ return; ++ } ++ ++ if (speed == 10) ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_CLK_2_5M); ++ else if (speed == 100) ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_CLK_25M); ++ else if (speed == 1000) ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_CLK_125M); ++ else ++ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); ++} ++ ++static void rk3368_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); ++ return; ++ } ++ ++ if (speed == 10) { ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_RMII_CLK_2_5M | ++ RK3368_GMAC_SPEED_10M); ++ } else if (speed == 100) { ++ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, ++ RK3368_GMAC_RMII_CLK_25M | ++ RK3368_GMAC_SPEED_100M); ++ } else { ++ dev_err(dev, "unknown speed value for RMII! speed=%d", speed); ++ } ++} ++ ++static const struct rk_gmac_ops rk3368_ops = { ++ .set_to_rgmii = rk3368_set_to_rgmii, ++ .set_to_rmii = rk3368_set_to_rmii, ++ .set_rgmii_speed = rk3368_set_rgmii_speed, ++ .set_rmii_speed = rk3368_set_rmii_speed, ++}; ++ ++static int gmac_clk_init(struct rk_priv_data *bsp_priv) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ bsp_priv->clk_enabled = false; ++ ++ bsp_priv->mac_clk_rx = devm_clk_get(dev, "mac_clk_rx"); ++ if (IS_ERR(bsp_priv->mac_clk_rx)) ++ dev_err(dev, "cannot get clock %s\n", ++ "mac_clk_rx"); ++ ++ bsp_priv->mac_clk_tx = devm_clk_get(dev, "mac_clk_tx"); ++ if (IS_ERR(bsp_priv->mac_clk_tx)) ++ dev_err(dev, "cannot get clock %s\n", ++ "mac_clk_tx"); ++ ++ bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac"); ++ if (IS_ERR(bsp_priv->aclk_mac)) ++ dev_err(dev, "cannot get clock %s\n", ++ "aclk_mac"); ++ ++ bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac"); ++ if (IS_ERR(bsp_priv->pclk_mac)) ++ dev_err(dev, "cannot get clock %s\n", ++ "pclk_mac"); ++ ++ bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth"); ++ if (IS_ERR(bsp_priv->clk_mac)) ++ dev_err(dev, "cannot get clock %s\n", ++ "stmmaceth"); ++ ++ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { ++ bsp_priv->clk_mac_ref = devm_clk_get(dev, "clk_mac_ref"); ++ if (IS_ERR(bsp_priv->clk_mac_ref)) ++ dev_err(dev, "cannot get clock %s\n", ++ "clk_mac_ref"); ++ ++ if (!bsp_priv->clock_input) { ++ bsp_priv->clk_mac_refout = ++ devm_clk_get(dev, "clk_mac_refout"); ++ if (IS_ERR(bsp_priv->clk_mac_refout)) ++ dev_err(dev, "cannot get clock %s\n", ++ "clk_mac_refout"); ++ } ++ } ++ ++ if (bsp_priv->clock_input) { ++ dev_info(dev, "clock input from PHY\n"); ++ } else { ++ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) ++ clk_set_rate(bsp_priv->clk_mac, 50000000); ++ } ++ ++ return 0; ++} ++ ++static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) ++{ ++ int phy_iface = phy_iface = bsp_priv->phy_iface; ++ ++ if (enable) { ++ if (!bsp_priv->clk_enabled) { ++ if (phy_iface == PHY_INTERFACE_MODE_RMII) { ++ if (!IS_ERR(bsp_priv->mac_clk_rx)) ++ clk_prepare_enable( ++ bsp_priv->mac_clk_rx); ++ ++ if (!IS_ERR(bsp_priv->clk_mac_ref)) ++ clk_prepare_enable( ++ bsp_priv->clk_mac_ref); ++ ++ if (!IS_ERR(bsp_priv->clk_mac_refout)) ++ clk_prepare_enable( ++ bsp_priv->clk_mac_refout); ++ } ++ ++ if (!IS_ERR(bsp_priv->aclk_mac)) ++ clk_prepare_enable(bsp_priv->aclk_mac); ++ ++ if (!IS_ERR(bsp_priv->pclk_mac)) ++ clk_prepare_enable(bsp_priv->pclk_mac); ++ ++ if (!IS_ERR(bsp_priv->mac_clk_tx)) ++ clk_prepare_enable(bsp_priv->mac_clk_tx); ++ ++ /** ++ * if (!IS_ERR(bsp_priv->clk_mac)) ++ * clk_prepare_enable(bsp_priv->clk_mac); ++ */ ++ mdelay(5); ++ bsp_priv->clk_enabled = true; ++ } ++ } else { ++ if (bsp_priv->clk_enabled) { ++ if (phy_iface == PHY_INTERFACE_MODE_RMII) { ++ if (!IS_ERR(bsp_priv->mac_clk_rx)) ++ clk_disable_unprepare( ++ bsp_priv->mac_clk_rx); ++ ++ if (!IS_ERR(bsp_priv->clk_mac_ref)) ++ clk_disable_unprepare( ++ bsp_priv->clk_mac_ref); ++ ++ if (!IS_ERR(bsp_priv->clk_mac_refout)) ++ clk_disable_unprepare( ++ bsp_priv->clk_mac_refout); ++ } ++ ++ if (!IS_ERR(bsp_priv->aclk_mac)) ++ clk_disable_unprepare(bsp_priv->aclk_mac); ++ ++ if (!IS_ERR(bsp_priv->pclk_mac)) ++ clk_disable_unprepare(bsp_priv->pclk_mac); ++ ++ if (!IS_ERR(bsp_priv->mac_clk_tx)) ++ clk_disable_unprepare(bsp_priv->mac_clk_tx); ++ /** ++ * if (!IS_ERR(bsp_priv->clk_mac)) ++ * clk_disable_unprepare(bsp_priv->clk_mac); ++ */ ++ bsp_priv->clk_enabled = false; ++ } ++ } ++ ++ return 0; ++} ++ ++static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) ++{ ++ struct regulator *ldo = bsp_priv->regulator; ++ int ret; ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (!ldo) { ++ dev_err(dev, "no regulator found\n"); ++ return -1; ++ } ++ ++ if (enable) { ++ ret = regulator_enable(ldo); ++ if (ret) ++ dev_err(dev, "fail to enable phy-supply\n"); ++ } else { ++ ret = regulator_disable(ldo); ++ if (ret) ++ dev_err(dev, "fail to disable phy-supply\n"); ++ } ++ ++ return 0; ++} ++ ++static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, ++ const struct rk_gmac_ops *ops) ++{ ++ struct rk_priv_data *bsp_priv; ++ struct device *dev = &pdev->dev; ++ int ret; ++ const char *strings = NULL; ++ int value; ++ ++ bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL); ++ if (!bsp_priv) ++ return ERR_PTR(-ENOMEM); ++ ++ bsp_priv->phy_iface = of_get_phy_mode(dev->of_node); ++ bsp_priv->ops = ops; ++ ++ bsp_priv->regulator = devm_regulator_get_optional(dev, "phy"); ++ if (IS_ERR(bsp_priv->regulator)) { ++ if (PTR_ERR(bsp_priv->regulator) == -EPROBE_DEFER) { ++ dev_err(dev, "phy regulator is not available yet, deferred probing\n"); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ dev_err(dev, "no regulator found\n"); ++ bsp_priv->regulator = NULL; ++ } ++ ++ ret = of_property_read_string(dev->of_node, "clock_in_out", &strings); ++ if (ret) { ++ dev_err(dev, "Can not read property: clock_in_out.\n"); ++ bsp_priv->clock_input = true; ++ } else { ++ dev_info(dev, "clock input or output? (%s).\n", ++ strings); ++ if (!strcmp(strings, "input")) ++ bsp_priv->clock_input = true; ++ else ++ bsp_priv->clock_input = false; ++ } ++ ++ ret = of_property_read_u32(dev->of_node, "tx_delay", &value); ++ if (ret) { ++ bsp_priv->tx_delay = 0x30; ++ dev_err(dev, "Can not read property: tx_delay."); ++ dev_err(dev, "set tx_delay to 0x%x\n", ++ bsp_priv->tx_delay); ++ } else { ++ dev_info(dev, "TX delay(0x%x).\n", value); ++ bsp_priv->tx_delay = value; ++ } ++ ++ ret = of_property_read_u32(dev->of_node, "rx_delay", &value); ++ if (ret) { ++ bsp_priv->rx_delay = 0x10; ++ dev_err(dev, "Can not read property: rx_delay."); ++ dev_err(dev, "set rx_delay to 0x%x\n", ++ bsp_priv->rx_delay); ++ } else { ++ dev_info(dev, "RX delay(0x%x).\n", value); ++ bsp_priv->rx_delay = value; ++ } ++ ++ bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "rockchip,grf"); ++ bsp_priv->pdev = pdev; ++ ++ /*rmii or rgmii*/ ++ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) { ++ dev_info(dev, "init for RGMII\n"); ++ bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, ++ bsp_priv->rx_delay); ++ } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { ++ dev_info(dev, "init for RMII\n"); ++ bsp_priv->ops->set_to_rmii(bsp_priv); ++ } else { ++ dev_err(dev, "NO interface defined!\n"); ++ } ++ ++ gmac_clk_init(bsp_priv); ++ ++ return bsp_priv; ++} ++ ++static int rk_gmac_init(struct platform_device *pdev, void *priv) ++{ ++ struct rk_priv_data *bsp_priv = priv; ++ int ret; ++ ++ ret = phy_power_on(bsp_priv, true); ++ if (ret) ++ return ret; ++ ++ ret = gmac_clk_enable(bsp_priv, true); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void rk_gmac_exit(struct platform_device *pdev, void *priv) ++{ ++ struct rk_priv_data *gmac = priv; ++ ++ phy_power_on(gmac, false); ++ gmac_clk_enable(gmac, false); ++} ++ ++static void rk_fix_speed(void *priv, unsigned int speed) ++{ ++ struct rk_priv_data *bsp_priv = priv; ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) ++ bsp_priv->ops->set_rgmii_speed(bsp_priv, speed); ++ else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) ++ bsp_priv->ops->set_rmii_speed(bsp_priv, speed); ++ else ++ dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); ++} ++ ++static int rk_gmac_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ const struct rk_gmac_ops *data; ++ int ret; ++ ++ data = of_device_get_match_data(&pdev->dev); ++ if (!data) { ++ dev_err(&pdev->dev, "no of match data provided\n"); ++ return -EINVAL; ++ } ++ ++ 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); ++ ++ plat_dat->has_gmac = true; ++ plat_dat->init = rk_gmac_init; ++ plat_dat->exit = rk_gmac_exit; ++ plat_dat->fix_mac_speed = rk_fix_speed; ++ ++ plat_dat->bsp_priv = rk_gmac_setup(pdev, data); ++ if (IS_ERR(plat_dat->bsp_priv)) ++ return PTR_ERR(plat_dat->bsp_priv); ++ ++ ret = rk_gmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++} ++ ++static const struct of_device_id rk_gmac_dwmac_match[] = { ++ { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops }, ++ { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); ++ ++static struct platform_driver rk_gmac_dwmac_driver = { ++ .probe = rk_gmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "rk_gmac-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = rk_gmac_dwmac_match, ++ }, ++}; ++module_platform_driver(rk_gmac_dwmac_driver); ++ ++MODULE_AUTHOR("Chen-Zhi (Roger Chen) <roger.chen@rock-chips.com>"); ++MODULE_DESCRIPTION("Rockchip RK3288 DWMAC specific glue layer"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +@@ -23,7 +23,9 @@ + #include <linux/regmap.h> + #include <linux/reset.h> + #include <linux/stmmac.h> ++ + #include "stmmac.h" ++#include "stmmac_platform.h" + + #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 + #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 +@@ -89,7 +91,9 @@ static int socfpga_dwmac_parse_data(stru + STMMAC_RESOURCE_NAME); + if (IS_ERR(dwmac->stmmac_rst)) { + dev_info(dev, "Could not get reset control!\n"); +- return -EINVAL; ++ if (PTR_ERR(dwmac->stmmac_rst) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dwmac->stmmac_rst = NULL; + } + + dwmac->interface = of_get_phy_mode(np); +@@ -171,31 +175,6 @@ static int socfpga_dwmac_setup(struct so + return 0; + } + +-static void *socfpga_dwmac_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- int ret; +- struct socfpga_dwmac *dwmac; +- +- dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); +- if (!dwmac) +- return ERR_PTR(-ENOMEM); +- +- ret = socfpga_dwmac_parse_data(dwmac, dev); +- if (ret) { +- dev_err(dev, "Unable to parse OF data\n"); +- return ERR_PTR(ret); +- } +- +- ret = socfpga_dwmac_setup(dwmac); +- if (ret) { +- dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); +- return ERR_PTR(ret); +- } +- +- return dwmac; +-} +- + static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv) + { + struct socfpga_dwmac *dwmac = priv; +@@ -253,9 +232,65 @@ static int socfpga_dwmac_init(struct pla + return ret; + } + +-const struct stmmac_of_data socfpga_gmac_data = { +- .setup = socfpga_dwmac_probe, +- .init = socfpga_dwmac_init, +- .exit = socfpga_dwmac_exit, +- .fix_mac_speed = socfpga_dwmac_fix_mac_speed, ++static int socfpga_dwmac_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ struct device *dev = &pdev->dev; ++ int ret; ++ struct socfpga_dwmac *dwmac; ++ ++ 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); ++ ++ dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); ++ if (!dwmac) ++ return -ENOMEM; ++ ++ ret = socfpga_dwmac_parse_data(dwmac, dev); ++ if (ret) { ++ dev_err(dev, "Unable to parse OF data\n"); ++ return ret; ++ } ++ ++ ret = socfpga_dwmac_setup(dwmac); ++ if (ret) { ++ dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); ++ return ret; ++ } ++ ++ plat_dat->bsp_priv = dwmac; ++ plat_dat->init = socfpga_dwmac_init; ++ plat_dat->exit = socfpga_dwmac_exit; ++ plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; ++ ++ ret = socfpga_dwmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++} ++ ++static const struct of_device_id socfpga_dwmac_match[] = { ++ { .compatible = "altr,socfpga-stmmac" }, ++ { } + }; ++MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); ++ ++static struct platform_driver socfpga_dwmac_driver = { ++ .probe = socfpga_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "socfpga-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = socfpga_dwmac_match, ++ }, ++}; ++module_platform_driver(socfpga_dwmac_driver); ++ ++MODULE_LICENSE("GPL v2"); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +@@ -1,4 +1,4 @@ +-/** ++/* + * dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer + * + * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited +@@ -17,11 +17,15 @@ + #include <linux/stmmac.h> + #include <linux/phy.h> + #include <linux/mfd/syscon.h> ++#include <linux/module.h> + #include <linux/regmap.h> + #include <linux/clk.h> + #include <linux/of.h> ++#include <linux/of_device.h> + #include <linux/of_net.h> + ++#include "stmmac_platform.h" ++ + #define DWMAC_125MHZ 125000000 + #define DWMAC_50MHZ 50000000 + #define DWMAC_25MHZ 25000000 +@@ -35,9 +39,8 @@ + #define IS_PHY_IF_MODE_GBIT(iface) (IS_PHY_IF_MODE_RGMII(iface) || \ + iface == PHY_INTERFACE_MODE_GMII) + +-/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) */ +- +-/** ++/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) ++ * + * Below table summarizes the clock requirement and clock sources for + * supported phy interface modes with link speeds. + * ________________________________________________ +@@ -76,9 +79,7 @@ + #define STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7) + #define STIH4XX_ETH_SEL_TXCLK_NOT_CLK125 BIT(6) + +-/* STiD127 register definitions */ +- +-/** ++/* STiD127 register definitions + *----------------------- + * src |BIT(6)| BIT(7)| + *----------------------- +@@ -104,13 +105,13 @@ + #define EN_MASK GENMASK(1, 1) + #define EN BIT(1) + +-/** ++/* + * 3 bits [4:2] + * 000-GMII/MII + * 001-RGMII + * 010-SGMII + * 100-RMII +-*/ ++ */ + #define MII_PHY_SEL_MASK GENMASK(4, 2) + #define ETH_PHY_SEL_RMII BIT(4) + #define ETH_PHY_SEL_SGMII BIT(3) +@@ -123,11 +124,16 @@ struct sti_dwmac { + bool ext_phyclk; /* Clock from external PHY */ + u32 tx_retime_src; /* TXCLK Retiming*/ + struct clk *clk; /* PHY clock */ +- int ctrl_reg; /* GMAC glue-logic control register */ ++ u32 ctrl_reg; /* GMAC glue-logic control register */ + int clk_sel_reg; /* GMAC ext clk selection register */ + struct device *dev; + struct regmap *regmap; + u32 speed; ++ void (*fix_retime_src)(void *priv, unsigned int speed); ++}; ++ ++struct sti_dwmac_of_data { ++ void (*fix_retime_src)(void *priv, unsigned int speed); + }; + + static u32 phy_intf_sels[] = { +@@ -222,8 +228,9 @@ static void stid127_fix_retime_src(void + regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val); + } + +-static void sti_dwmac_ctrl_init(struct sti_dwmac *dwmac) ++static int sti_dwmac_init(struct platform_device *pdev, void *priv) + { ++ struct sti_dwmac *dwmac = priv; + struct regmap *regmap = dwmac->regmap; + int iface = dwmac->interface; + struct device *dev = dwmac->dev; +@@ -241,28 +248,8 @@ static void sti_dwmac_ctrl_init(struct s + + val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII; + regmap_update_bits(regmap, reg, ENMII_MASK, val); +-} + +-static int stix4xx_init(struct platform_device *pdev, void *priv) +-{ +- struct sti_dwmac *dwmac = priv; +- u32 spd = dwmac->speed; +- +- sti_dwmac_ctrl_init(dwmac); +- +- stih4xx_fix_retime_src(priv, spd); +- +- return 0; +-} +- +-static int stid127_init(struct platform_device *pdev, void *priv) +-{ +- struct sti_dwmac *dwmac = priv; +- u32 spd = dwmac->speed; +- +- sti_dwmac_ctrl_init(dwmac); +- +- stid127_fix_retime_src(priv, spd); ++ dwmac->fix_retime_src(priv, dwmac->speed); + + return 0; + } +@@ -286,11 +273,6 @@ static int sti_dwmac_parse_data(struct s + if (!np) + return -EINVAL; + +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-ethconf"); +- if (!res) +- return -ENODATA; +- dwmac->ctrl_reg = res->start; +- + /* clk selection from extra syscfg register */ + dwmac->clk_sel_reg = -ENXIO; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf"); +@@ -301,6 +283,12 @@ static int sti_dwmac_parse_data(struct s + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + ++ err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->ctrl_reg); ++ if (err) { ++ dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n", err); ++ return err; ++ } ++ + dwmac->dev = dev; + dwmac->interface = of_get_phy_mode(np); + dwmac->regmap = regmap; +@@ -310,16 +298,16 @@ static int sti_dwmac_parse_data(struct s + + if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) { + const char *rs; +- dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN; + + err = of_property_read_string(np, "st,tx-retime-src", &rs); +- if (err < 0) ++ if (err < 0) { + dev_warn(dev, "Use internal clock source\n"); +- +- if (!strcasecmp(rs, "clk_125")) ++ dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN; ++ } else if (!strcasecmp(rs, "clk_125")) { + dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125; +- else if (!strcasecmp(rs, "txclk")) ++ } else if (!strcasecmp(rs, "txclk")) { + dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK; ++ } + + dwmac->speed = SPEED_1000; + } +@@ -333,34 +321,80 @@ static int sti_dwmac_parse_data(struct s + return 0; + } + +-static void *sti_dwmac_setup(struct platform_device *pdev) ++static int sti_dwmac_probe(struct platform_device *pdev) + { ++ struct plat_stmmacenet_data *plat_dat; ++ const struct sti_dwmac_of_data *data; ++ struct stmmac_resources stmmac_res; + struct sti_dwmac *dwmac; + int ret; + ++ data = of_device_get_match_data(&pdev->dev); ++ if (!data) { ++ dev_err(&pdev->dev, "No OF match data provided\n"); ++ return -EINVAL; ++ } ++ ++ 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); ++ + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) +- return ERR_PTR(-ENOMEM); ++ return -ENOMEM; + + ret = sti_dwmac_parse_data(dwmac, pdev); + if (ret) { + dev_err(&pdev->dev, "Unable to parse OF data\n"); +- return ERR_PTR(ret); ++ return ret; + } + +- return dwmac; ++ dwmac->fix_retime_src = data->fix_retime_src; ++ ++ plat_dat->bsp_priv = dwmac; ++ plat_dat->init = sti_dwmac_init; ++ plat_dat->exit = sti_dwmac_exit; ++ plat_dat->fix_mac_speed = data->fix_retime_src; ++ ++ ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + } + +-const struct stmmac_of_data stih4xx_dwmac_data = { +- .fix_mac_speed = stih4xx_fix_retime_src, +- .setup = sti_dwmac_setup, +- .init = stix4xx_init, +- .exit = sti_dwmac_exit, ++static const struct sti_dwmac_of_data stih4xx_dwmac_data = { ++ .fix_retime_src = stih4xx_fix_retime_src, + }; + +-const struct stmmac_of_data stid127_dwmac_data = { +- .fix_mac_speed = stid127_fix_retime_src, +- .setup = sti_dwmac_setup, +- .init = stid127_init, +- .exit = sti_dwmac_exit, ++static const struct sti_dwmac_of_data stid127_dwmac_data = { ++ .fix_retime_src = stid127_fix_retime_src, + }; ++ ++static const struct of_device_id sti_dwmac_match[] = { ++ { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, ++ { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, ++ { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, ++ { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, sti_dwmac_match); ++ ++static struct platform_driver sti_dwmac_driver = { ++ .probe = sti_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "sti-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = sti_dwmac_match, ++ }, ++}; ++module_platform_driver(sti_dwmac_driver); ++ ++MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@st.com>"); ++MODULE_DESCRIPTION("STMicroelectronics DWMAC Specific Glue layer"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c +@@ -1,4 +1,4 @@ +-/** ++/* + * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer + * + * Copyright (C) 2013 Chen-Yu Tsai +@@ -18,10 +18,14 @@ + + #include <linux/stmmac.h> + #include <linux/clk.h> ++#include <linux/module.h> + #include <linux/phy.h> ++#include <linux/platform_device.h> + #include <linux/of_net.h> + #include <linux/regulator/consumer.h> + ++#include "stmmac_platform.h" ++ + struct sunxi_priv_data { + int interface; + int clk_enabled; +@@ -29,35 +33,6 @@ struct sunxi_priv_data { + struct regulator *regulator; + }; + +-static void *sun7i_gmac_setup(struct platform_device *pdev) +-{ +- struct sunxi_priv_data *gmac; +- struct device *dev = &pdev->dev; +- +- gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); +- if (!gmac) +- return ERR_PTR(-ENOMEM); +- +- gmac->interface = of_get_phy_mode(dev->of_node); +- +- gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); +- if (IS_ERR(gmac->tx_clk)) { +- dev_err(dev, "could not get tx clock\n"); +- return 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 ERR_PTR(-EPROBE_DEFER); +- dev_info(dev, "no regulator found\n"); +- gmac->regulator = NULL; +- } +- +- return gmac; +-} +- + #define SUN7I_GMAC_GMII_RGMII_RATE 125000000 + #define SUN7I_GMAC_MII_RATE 25000000 + +@@ -128,13 +103,76 @@ static void sun7i_fix_speed(void *priv, + } + } + +-/* of_data specifying hardware features and callbacks. +- * hardware features were copied from Allwinner drivers. */ +-const struct stmmac_of_data sun7i_gmac_data = { +- .has_gmac = 1, +- .tx_coe = 1, +- .fix_mac_speed = sun7i_fix_speed, +- .setup = sun7i_gmac_setup, +- .init = sun7i_gmac_init, +- .exit = sun7i_gmac_exit, ++static int sun7i_gmac_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->interface = of_get_phy_mode(dev->of_node); ++ ++ gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); ++ 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; ++ } ++ ++ /* platform data specifying hardware features and callbacks. ++ * hardware features were copied from Allwinner drivers. */ ++ plat_dat->tx_coe = 1; ++ plat_dat->has_gmac = true; ++ plat_dat->bsp_priv = gmac; ++ plat_dat->init = sun7i_gmac_init; ++ plat_dat->exit = sun7i_gmac_exit; ++ plat_dat->fix_mac_speed = sun7i_fix_speed; ++ ++ ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++} ++ ++static const struct of_device_id sun7i_dwmac_match[] = { ++ { .compatible = "allwinner,sun7i-a20-gmac" }, ++ { } + }; ++MODULE_DEVICE_TABLE(of, sun7i_dwmac_match); ++ ++static struct platform_driver sun7i_dwmac_driver = { ++ .probe = sun7i_gmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "sun7i-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = sun7i_dwmac_match, ++ }, ++}; ++module_platform_driver(sun7i_dwmac_driver); ++ ++MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); ++MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +@@ -172,6 +172,7 @@ enum inter_frame_gap { + /* GMAC FLOW CTRL defines */ + #define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ + #define GMAC_FLOW_CTRL_PT_SHIFT 16 ++#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */ + #define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ + #define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ + #define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ +@@ -246,6 +247,56 @@ enum ttc_control { + #define DMA_CONTROL_FEF 0x00000080 + #define DMA_CONTROL_FUF 0x00000040 + ++/* Receive flow control activation field ++ * RFA field in DMA control register, bits 23,10:9 ++ */ ++#define DMA_CONTROL_RFA_MASK 0x00800600 ++ ++/* Receive flow control deactivation field ++ * RFD field in DMA control register, bits 22,12:11 ++ */ ++#define DMA_CONTROL_RFD_MASK 0x00401800 ++ ++/* RFD and RFA fields are encoded as follows ++ * ++ * Bit Field ++ * 0,00 - Full minus 1KB (only valid when rxfifo >= 4KB and EFC enabled) ++ * 0,01 - Full minus 2KB (only valid when rxfifo >= 4KB and EFC enabled) ++ * 0,10 - Full minus 3KB (only valid when rxfifo >= 4KB and EFC enabled) ++ * 0,11 - Full minus 4KB (only valid when rxfifo > 4KB and EFC enabled) ++ * 1,00 - Full minus 5KB (only valid when rxfifo > 8KB and EFC enabled) ++ * 1,01 - Full minus 6KB (only valid when rxfifo > 8KB and EFC enabled) ++ * 1,10 - Full minus 7KB (only valid when rxfifo > 8KB and EFC enabled) ++ * 1,11 - Reserved ++ * ++ * RFD should always be > RFA for a given FIFO size. RFD == RFA may work, ++ * but packet throughput performance may not be as expected. ++ * ++ * Be sure that bit 3 in GMAC Register 6 is set for Unicast Pause frame ++ * detection (IEEE Specification Requirement, Annex 31B, 31B.1, Pause ++ * Description). ++ * ++ * Be sure that DZPA (bit 7 in Flow Control Register, GMAC Register 6), ++ * is set to 0. This allows pause frames with a quanta of 0 to be sent ++ * as an XOFF message to the link peer. ++ */ ++ ++#define RFA_FULL_MINUS_1K 0x00000000 ++#define RFA_FULL_MINUS_2K 0x00000200 ++#define RFA_FULL_MINUS_3K 0x00000400 ++#define RFA_FULL_MINUS_4K 0x00000600 ++#define RFA_FULL_MINUS_5K 0x00800000 ++#define RFA_FULL_MINUS_6K 0x00800200 ++#define RFA_FULL_MINUS_7K 0x00800400 ++ ++#define RFD_FULL_MINUS_1K 0x00000000 ++#define RFD_FULL_MINUS_2K 0x00000800 ++#define RFD_FULL_MINUS_3K 0x00001000 ++#define RFD_FULL_MINUS_4K 0x00001800 ++#define RFD_FULL_MINUS_5K 0x00400000 ++#define RFD_FULL_MINUS_6K 0x00400800 ++#define RFD_FULL_MINUS_7K 0x00401000 ++ + enum rtc_control { + DMA_CONTROL_RTC_64 = 0x00000000, + DMA_CONTROL_RTC_32 = 0x00000008, +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +@@ -201,7 +201,10 @@ static void dwmac1000_flow_ctrl(struct m + unsigned int fc, unsigned int pause_time) + { + void __iomem *ioaddr = hw->pcsr; +- unsigned int flow = 0; ++ /* Set flow such that DZPQ in Mac Register 6 is 0, ++ * and unicast pause detect is enabled. ++ */ ++ unsigned int flow = GMAC_FLOW_CTRL_UP; + + pr_debug("GMAC Flow-Control:\n"); + if (fc & FLOW_RX) { +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +@@ -70,10 +70,6 @@ static int dwmac1000_dma_init(void __iom + if (mb) + value |= DMA_BUS_MODE_MB; + +-#ifdef CONFIG_STMMAC_DA +- value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */ +-#endif +- + if (atds) + value |= DMA_BUS_MODE_ATDS; + +@@ -110,8 +106,29 @@ static int dwmac1000_dma_init(void __iom + return 0; + } + ++static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz) ++{ ++ csr6 &= ~DMA_CONTROL_RFA_MASK; ++ csr6 &= ~DMA_CONTROL_RFD_MASK; ++ ++ /* Leave flow control disabled if receive fifo size is less than ++ * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full, ++ * and send XON when 2K less than full. ++ */ ++ if (rxfifosz < 4096) { ++ csr6 &= ~DMA_CONTROL_EFC; ++ pr_debug("GMAC: disabling flow control, rxfifo too small(%d)\n", ++ rxfifosz); ++ } else { ++ csr6 |= DMA_CONTROL_EFC; ++ csr6 |= RFA_FULL_MINUS_1K; ++ csr6 |= RFD_FULL_MINUS_2K; ++ } ++ return csr6; ++} ++ + static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, +- int rxmode) ++ int rxmode, int rxfifosz) + { + u32 csr6 = readl(ioaddr + DMA_CONTROL); + +@@ -157,6 +174,9 @@ static void dwmac1000_dma_operation_mode + csr6 |= DMA_CONTROL_RTC_128; + } + ++ /* Configure flow control based on rx fifo size */ ++ csr6 = dwmac1000_configure_fc(csr6, rxfifosz); ++ + writel(csr6, ioaddr + DMA_CONTROL); + } + +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +@@ -72,7 +72,7 @@ static int dwmac100_dma_init(void __iome + * control register. + */ + static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode, +- int rxmode) ++ int rxmode, int rxfifosz) + { + u32 csr6 = readl(ioaddr + DMA_CONTROL); + +--- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c +@@ -73,7 +73,7 @@ + #define MMC_RX_OCTETCOUNT_G 0x00000188 + #define MMC_RX_BROADCASTFRAME_G 0x0000018c + #define MMC_RX_MULTICASTFRAME_G 0x00000190 +-#define MMC_RX_CRC_ERRROR 0x00000194 ++#define MMC_RX_CRC_ERROR 0x00000194 + #define MMC_RX_ALIGN_ERROR 0x00000198 + #define MMC_RX_RUN_ERROR 0x0000019C + #define MMC_RX_JABBER_ERROR 0x000001A0 +@@ -196,7 +196,7 @@ void dwmac_mmc_read(void __iomem *ioaddr + mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G); + mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G); + mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G); +- mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERRROR); ++ mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERROR); + mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR); + mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR); + mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR); +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -34,6 +34,14 @@ + #include <linux/ptp_clock_kernel.h> + #include <linux/reset.h> + ++struct stmmac_resources { ++ void __iomem *addr; ++ const char *mac; ++ int wol_irq; ++ int lpi_irq; ++ int irq; ++}; ++ + struct stmmac_tx_info { + dma_addr_t buf; + bool map_as_page; +@@ -97,6 +105,7 @@ struct stmmac_priv { + int wolopts; + int wol_irq; + struct clk *stmmac_clk; ++ struct clk *pclk; + struct reset_control *stmmac_rst; + int clk_csr; + struct timer_list eee_ctrl_timer; +@@ -116,97 +125,28 @@ struct stmmac_priv { + int use_riwt; + int irq_wake; + spinlock_t ptp_lock; ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *dbgfs_dir; ++ struct dentry *dbgfs_rings_status; ++ struct dentry *dbgfs_dma_cap; ++#endif + }; + + int stmmac_mdio_unregister(struct net_device *ndev); + int stmmac_mdio_register(struct net_device *ndev); + int stmmac_mdio_reset(struct mii_bus *mii); + void stmmac_set_ethtool_ops(struct net_device *netdev); +-extern const struct stmmac_desc_ops enh_desc_ops; +-extern const struct stmmac_desc_ops ndesc_ops; +-extern const struct stmmac_hwtimestamp stmmac_ptp; ++ + int stmmac_ptp_register(struct stmmac_priv *priv); + void stmmac_ptp_unregister(struct stmmac_priv *priv); + int stmmac_resume(struct net_device *ndev); + int stmmac_suspend(struct net_device *ndev); + int stmmac_dvr_remove(struct net_device *ndev); +-struct stmmac_priv *stmmac_dvr_probe(struct device *device, +- struct plat_stmmacenet_data *plat_dat, +- void __iomem *addr); ++int stmmac_dvr_probe(struct device *device, ++ struct plat_stmmacenet_data *plat_dat, ++ struct stmmac_resources *res); + void stmmac_disable_eee_mode(struct stmmac_priv *priv); + bool stmmac_eee_init(struct stmmac_priv *priv); + +-#ifdef CONFIG_STMMAC_PLATFORM +-#ifdef CONFIG_DWMAC_MESON +-extern const struct stmmac_of_data meson6_dwmac_data; +-#endif +-#ifdef CONFIG_DWMAC_SUNXI +-extern const struct stmmac_of_data sun7i_gmac_data; +-#endif +-#ifdef CONFIG_DWMAC_STI +-extern const struct stmmac_of_data stih4xx_dwmac_data; +-extern const struct stmmac_of_data stid127_dwmac_data; +-#endif +-#ifdef CONFIG_DWMAC_SOCFPGA +-extern const struct stmmac_of_data socfpga_gmac_data; +-#endif +-extern struct platform_driver stmmac_pltfr_driver; +-static inline int stmmac_register_platform(void) +-{ +- int err; +- +- err = platform_driver_register(&stmmac_pltfr_driver); +- if (err) +- pr_err("stmmac: failed to register the platform driver\n"); +- +- return err; +-} +- +-static inline void stmmac_unregister_platform(void) +-{ +- platform_driver_unregister(&stmmac_pltfr_driver); +-} +-#else +-static inline int stmmac_register_platform(void) +-{ +- pr_debug("stmmac: do not register the platf driver\n"); +- +- return 0; +-} +- +-static inline void stmmac_unregister_platform(void) +-{ +-} +-#endif /* CONFIG_STMMAC_PLATFORM */ +- +-#ifdef CONFIG_STMMAC_PCI +-extern struct pci_driver stmmac_pci_driver; +-static inline int stmmac_register_pci(void) +-{ +- int err; +- +- err = pci_register_driver(&stmmac_pci_driver); +- if (err) +- pr_err("stmmac: failed to register the PCI driver\n"); +- +- return err; +-} +- +-static inline void stmmac_unregister_pci(void) +-{ +- pci_unregister_driver(&stmmac_pci_driver); +-} +-#else +-static inline int stmmac_register_pci(void) +-{ +- pr_debug("stmmac: do not register the PCI driver\n"); +- +- return 0; +-} +- +-static inline void stmmac_unregister_pci(void) +-{ +-} +-#endif /* CONFIG_STMMAC_PCI */ +- + #endif /* __STMMAC_H__ */ +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -696,7 +696,7 @@ static int stmmac_set_coalesce(struct ne + (ec->tx_max_coalesced_frames == 0)) + return -EINVAL; + +- if ((ec->tx_coalesce_usecs > STMMAC_COAL_TX_TIMER) || ++ if ((ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK) || + (ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES)) + return -EINVAL; + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -44,14 +44,15 @@ + #include <linux/slab.h> + #include <linux/prefetch.h> + #include <linux/pinctrl/consumer.h> +-#ifdef CONFIG_STMMAC_DEBUG_FS ++#ifdef CONFIG_DEBUG_FS + #include <linux/debugfs.h> + #include <linux/seq_file.h> +-#endif /* CONFIG_STMMAC_DEBUG_FS */ ++#endif /* CONFIG_DEBUG_FS */ + #include <linux/net_tstamp.h> + #include "stmmac_ptp.h" + #include "stmmac.h" + #include <linux/reset.h> ++#include <linux/of_mdio.h> + + #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) + +@@ -116,17 +117,17 @@ MODULE_PARM_DESC(chain_mode, "To use cha + + static irqreturn_t stmmac_interrupt(int irq, void *dev_id); + +-#ifdef CONFIG_STMMAC_DEBUG_FS ++#ifdef CONFIG_DEBUG_FS + static int stmmac_init_fs(struct net_device *dev); +-static void stmmac_exit_fs(void); ++static void stmmac_exit_fs(struct net_device *dev); + #endif + + #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) + + /** + * stmmac_verify_args - verify the driver parameters. +- * Description: it verifies if some wrong parameter is passed to the driver. +- * Note that wrong parameters are replaced with the default values. ++ * Description: it checks the driver parameters and set a default in case of ++ * errors. + */ + static void stmmac_verify_args(void) + { +@@ -191,14 +192,8 @@ static void stmmac_clk_csr_set(struct st + + static void print_pkt(unsigned char *buf, int len) + { +- int j; +- pr_debug("len = %d byte, buf addr: 0x%p", len, buf); +- for (j = 0; j < len; j++) { +- if ((j % 16) == 0) +- pr_debug("\n %03x:", j); +- pr_debug(" %02x", buf[j]); +- } +- pr_debug("\n"); ++ pr_debug("len = %d byte, buf addr: 0x%p\n", len, buf); ++ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); + } + + /* minimum number of free TX descriptors required to wake up TX process */ +@@ -210,7 +205,7 @@ static inline u32 stmmac_tx_avail(struct + } + + /** +- * stmmac_hw_fix_mac_speed: callback for speed selection ++ * stmmac_hw_fix_mac_speed - callback for speed selection + * @priv: driver private structure + * Description: on some platforms (e.g. ST), some HW system configuraton + * registers have to be set according to the link speed negotiated. +@@ -224,9 +219,10 @@ static inline void stmmac_hw_fix_mac_spe + } + + /** +- * stmmac_enable_eee_mode: Check and enter in LPI mode ++ * stmmac_enable_eee_mode - check and enter in LPI mode + * @priv: driver private structure +- * Description: this function is to verify and enter in LPI mode for EEE. ++ * Description: this function is to verify and enter in LPI mode in case of ++ * EEE. + */ + static void stmmac_enable_eee_mode(struct stmmac_priv *priv) + { +@@ -237,7 +233,7 @@ static void stmmac_enable_eee_mode(struc + } + + /** +- * stmmac_disable_eee_mode: disable/exit from EEE ++ * stmmac_disable_eee_mode - disable and exit from LPI mode + * @priv: driver private structure + * Description: this function is to exit and disable EEE in case of + * LPI state is true. This is called by the xmit. +@@ -250,7 +246,7 @@ void stmmac_disable_eee_mode(struct stmm + } + + /** +- * stmmac_eee_ctrl_timer: EEE TX SW timer. ++ * stmmac_eee_ctrl_timer - EEE TX SW timer. + * @arg : data hook + * Description: + * if there is no data transfer and if we are not in LPI state, +@@ -265,13 +261,12 @@ static void stmmac_eee_ctrl_timer(unsign + } + + /** +- * stmmac_eee_init: init EEE ++ * stmmac_eee_init - init EEE + * @priv: driver private structure + * Description: +- * If the EEE support has been enabled while configuring the driver, +- * if the GMAC actually supports the EEE (from the HW cap reg) and the +- * phy can also manage EEE, so enable the LPI state and start the timer +- * to verify if the tx path can enter in LPI state. ++ * if the GMAC supports the EEE (from the HW cap reg) and the phy device ++ * can also manage EEE, this function enable the LPI state and start related ++ * timer. + */ + bool stmmac_eee_init(struct stmmac_priv *priv) + { +@@ -316,11 +311,11 @@ bool stmmac_eee_init(struct stmmac_priv + spin_lock_irqsave(&priv->lock, flags); + if (!priv->eee_active) { + priv->eee_active = 1; +- init_timer(&priv->eee_ctrl_timer); +- priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer; +- priv->eee_ctrl_timer.data = (unsigned long)priv; +- priv->eee_ctrl_timer.expires = STMMAC_LPI_T(eee_timer); +- add_timer(&priv->eee_ctrl_timer); ++ setup_timer(&priv->eee_ctrl_timer, ++ stmmac_eee_ctrl_timer, ++ (unsigned long)priv); ++ mod_timer(&priv->eee_ctrl_timer, ++ STMMAC_LPI_T(eee_timer)); + + priv->hw->mac->set_eee_timer(priv->hw, + STMMAC_DEFAULT_LIT_LS, +@@ -338,7 +333,7 @@ out: + return ret; + } + +-/* stmmac_get_tx_hwtstamp: get HW TX timestamps ++/* stmmac_get_tx_hwtstamp - get HW TX timestamps + * @priv: driver private structure + * @entry : descriptor index to be used. + * @skb : the socket buffer +@@ -380,7 +375,7 @@ static void stmmac_get_tx_hwtstamp(struc + return; + } + +-/* stmmac_get_rx_hwtstamp: get HW RX timestamps ++/* stmmac_get_rx_hwtstamp - get HW RX timestamps + * @priv: driver private structure + * @entry : descriptor index to be used. + * @skb : the socket buffer +@@ -615,7 +610,7 @@ static int stmmac_hwtstamp_ioctl(struct + * where, freq_div_ratio = clk_ptp_ref_i/50MHz + * hence, addend = ((2^32) * 50MHz)/clk_ptp_ref_i; + * NOTE: clk_ptp_ref_i should be >= 50MHz to +- * achive 20ns accuracy. ++ * achieve 20ns accuracy. + * + * 2^x * y == (y << x), hence + * 2^32 * 50000000 ==> (50000000 << 32) +@@ -636,11 +631,11 @@ static int stmmac_hwtstamp_ioctl(struct + } + + /** +- * stmmac_init_ptp: init PTP ++ * stmmac_init_ptp - init PTP + * @priv: driver private structure +- * Description: this is to verify if the HW supports the PTPv1 or v2. ++ * Description: this is to verify if the HW supports the PTPv1 or PTPv2. + * This is done by looking at the HW cap. register. +- * Also it registers the ptp driver. ++ * This function also registers the ptp driver. + */ + static int stmmac_init_ptp(struct stmmac_priv *priv) + { +@@ -682,9 +677,13 @@ static void stmmac_release_ptp(struct st + } + + /** +- * stmmac_adjust_link ++ * stmmac_adjust_link - adjusts the link parameters + * @dev: net device structure +- * Description: it adjusts the link parameters. ++ * Description: this is the helper called by the physical abstraction layer ++ * drivers to communicate the phy link status. According the speed and duplex ++ * this driver can invoke registered glue-logic as well. ++ * It also invoke the eee initialization because it could happen when switch ++ * on different networks (that are eee capable). + */ + static void stmmac_adjust_link(struct net_device *dev) + { +@@ -774,7 +773,7 @@ static void stmmac_adjust_link(struct ne + } + + /** +- * stmmac_check_pcs_mode: verify if RGMII/SGMII is supported ++ * stmmac_check_pcs_mode - verify if RGMII/SGMII is supported + * @priv: driver private structure + * Description: this is to verify if the HW supports the PCS. + * Physical Coding Sublayer (PCS) interface that can be used when the MAC is +@@ -818,21 +817,31 @@ static int stmmac_init_phy(struct net_de + priv->speed = 0; + priv->oldduplex = -1; + +- if (priv->plat->phy_bus_name) +- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", +- priv->plat->phy_bus_name, priv->plat->bus_id); +- else +- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", +- priv->plat->bus_id); ++ if (priv->plat->phy_node) { ++ phydev = of_phy_connect(dev, priv->plat->phy_node, ++ &stmmac_adjust_link, 0, interface); ++ } else { ++ if (priv->plat->phy_bus_name) ++ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", ++ priv->plat->phy_bus_name, priv->plat->bus_id); ++ else ++ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", ++ priv->plat->bus_id); + +- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, +- priv->plat->phy_addr); +- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); ++ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, ++ priv->plat->phy_addr); ++ pr_debug("stmmac_init_phy: trying to attach to %s\n", ++ phy_id_fmt); + +- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); ++ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, ++ interface); ++ } + +- if (IS_ERR(phydev)) { ++ if (IS_ERR_OR_NULL(phydev)) { + pr_err("%s: Could not attach to PHY\n", dev->name); ++ if (!phydev) ++ return -ENODEV; ++ + return PTR_ERR(phydev); + } + +@@ -850,7 +859,7 @@ static int stmmac_init_phy(struct net_de + * device as well. + * Note: phydev->phy_id is the result of reading the UID PHY registers. + */ +- if (phydev->phy_id == 0) { ++ if (!priv->plat->phy_node && phydev->phy_id == 0) { + phy_disconnect(phydev); + return -ENODEV; + } +@@ -863,7 +872,7 @@ static int stmmac_init_phy(struct net_de + } + + /** +- * stmmac_display_ring: display ring ++ * stmmac_display_ring - display ring + * @head: pointer to the head of the ring passed. + * @size: size of the ring. + * @extend_desc: to verify if extended descriptors are used. +@@ -931,7 +940,7 @@ static int stmmac_set_bfsize(int mtu, in + } + + /** +- * stmmac_clear_descriptors: clear descriptors ++ * stmmac_clear_descriptors - clear descriptors + * @priv: driver private structure + * Description: this function is called to clear the tx and rx descriptors + * in case of both basic and extended descriptors are used. +@@ -963,18 +972,25 @@ static void stmmac_clear_descriptors(str + (i == txsize - 1)); + } + ++/** ++ * stmmac_init_rx_buffers - init the RX descriptor buffer. ++ * @priv: driver private structure ++ * @p: descriptor pointer ++ * @i: descriptor index ++ * @flags: gfp flag. ++ * Description: this function is called to allocate a receive buffer, perform ++ * the DMA mapping and init the descriptor. ++ */ + static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, + int i, gfp_t flags) + { + struct sk_buff *skb; + +- skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN, +- flags); ++ skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); + if (!skb) { + pr_err("%s: Rx init fails; skb is NULL\n", __func__); + return -ENOMEM; + } +- skb_reserve(skb, NET_IP_ALIGN); + priv->rx_skbuff[i] = skb; + priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, + priv->dma_buf_sz, +@@ -1007,7 +1023,8 @@ static void stmmac_free_rx_buffers(struc + /** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure +- * Description: this function initializes the DMA RX/TX descriptors ++ * @flags: gfp flag. ++ * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It suppors the chained and ring + * modes. + */ +@@ -1089,6 +1106,7 @@ static int init_dma_desc_rings(struct ne + + priv->dirty_tx = 0; + priv->cur_tx = 0; ++ netdev_reset_queue(priv->dev); + + stmmac_clear_descriptors(priv); + +@@ -1144,6 +1162,14 @@ static void dma_free_tx_skbufs(struct st + } + } + ++/** ++ * alloc_dma_desc_resources - alloc TX/RX resources. ++ * @priv: private structure ++ * Description: according to which descriptor can be used (extend or basic) ++ * this function allocates the resources for TX and RX paths. In case of ++ * reception, for example, it pre-allocated the RX socket buffer in order to ++ * allow zero-copy mechanism. ++ */ + static int alloc_dma_desc_resources(struct stmmac_priv *priv) + { + unsigned int txsize = priv->dma_tx_size; +@@ -1255,13 +1281,15 @@ static void free_dma_desc_resources(stru + /** + * stmmac_dma_operation_mode - HW DMA operation mode + * @priv: driver private structure +- * Description: it sets the DMA operation mode: tx/rx DMA thresholds +- * or Store-And-Forward capability. ++ * Description: it is used for configuring the DMA operation mode register in ++ * order to program the tx/rx DMA thresholds or Store-And-Forward mode. + */ + static void stmmac_dma_operation_mode(struct stmmac_priv *priv) + { ++ int rxfifosz = priv->plat->rx_fifo_size; ++ + if (priv->plat->force_thresh_dma_mode) +- priv->hw->dma->dma_mode(priv->ioaddr, tc, tc); ++ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz); + else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { + /* + * In case of GMAC, SF mode can be enabled +@@ -1270,20 +1298,23 @@ static void stmmac_dma_operation_mode(st + * 2) There is no bugged Jumbo frame support + * that needs to not insert csum in the TDES. + */ +- priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE); +- tc = SF_DMA_MODE; ++ priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE, ++ rxfifosz); ++ priv->xstats.threshold = SF_DMA_MODE; + } else +- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE); ++ priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE, ++ rxfifosz); + } + + /** +- * stmmac_tx_clean: ++ * stmmac_tx_clean - to manage the transmission completion + * @priv: driver private structure +- * Description: it reclaims resources after transmission completes. ++ * Description: it reclaims the transmit resources after transmission completes. + */ + static void stmmac_tx_clean(struct stmmac_priv *priv) + { + unsigned int txsize = priv->dma_tx_size; ++ unsigned int bytes_compl = 0, pkts_compl = 0; + + spin_lock(&priv->tx_lock); + +@@ -1340,6 +1371,8 @@ static void stmmac_tx_clean(struct stmma + priv->hw->mode->clean_desc3(priv, p); + + if (likely(skb != NULL)) { ++ pkts_compl++; ++ bytes_compl += skb->len; + dev_consume_skb_any(skb); + priv->tx_skbuff[entry] = NULL; + } +@@ -1348,6 +1381,9 @@ static void stmmac_tx_clean(struct stmma + + priv->dirty_tx++; + } ++ ++ netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); ++ + if (unlikely(netif_queue_stopped(priv->dev) && + stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) { + netif_tx_lock(priv->dev); +@@ -1378,10 +1414,10 @@ static inline void stmmac_disable_dma_ir + } + + /** +- * stmmac_tx_err: irq tx error mng function ++ * stmmac_tx_err - to manage the tx error + * @priv: driver private structure + * Description: it cleans the descriptors and restarts the transmission +- * in case of errors. ++ * in case of transmission errors. + */ + static void stmmac_tx_err(struct stmmac_priv *priv) + { +@@ -1402,6 +1438,7 @@ static void stmmac_tx_err(struct stmmac_ + (i == txsize - 1)); + priv->dirty_tx = 0; + priv->cur_tx = 0; ++ netdev_reset_queue(priv->dev); + priv->hw->dma->start_tx(priv->ioaddr); + + priv->dev->stats.tx_errors++; +@@ -1409,16 +1446,16 @@ static void stmmac_tx_err(struct stmmac_ + } + + /** +- * stmmac_dma_interrupt: DMA ISR ++ * stmmac_dma_interrupt - DMA ISR + * @priv: driver private structure + * Description: this is the DMA ISR. It is called by the main ISR. +- * It calls the dwmac dma routine to understand which type of interrupt +- * happened. In case of there is a Normal interrupt and either TX or RX +- * interrupt happened so the NAPI is scheduled. ++ * It calls the dwmac dma routine and schedule poll method in case of some ++ * work can be done. + */ + static void stmmac_dma_interrupt(struct stmmac_priv *priv) + { + int status; ++ int rxfifosz = priv->plat->rx_fifo_size; + + status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); + if (likely((status & handle_rx)) || (status & handle_tx)) { +@@ -1429,9 +1466,15 @@ static void stmmac_dma_interrupt(struct + } + if (unlikely(status & tx_hard_error_bump_tc)) { + /* Try to bump up the dma threshold on this failure */ +- if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) { ++ if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && ++ (tc <= 256)) { + tc += 64; +- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE); ++ if (priv->plat->force_thresh_dma_mode) ++ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, ++ rxfifosz); ++ else ++ priv->hw->dma->dma_mode(priv->ioaddr, tc, ++ SF_DMA_MODE, rxfifosz); + priv->xstats.threshold = tc; + } + } else if (unlikely(status == tx_hard_error)) +@@ -1457,6 +1500,12 @@ static void stmmac_mmc_setup(struct stmm + pr_info(" No MAC Management Counters available\n"); + } + ++/** ++ * stmmac_get_synopsys_id - return the SYINID. ++ * @priv: driver private structure ++ * Description: this simple function is to decode and return the SYINID ++ * starting from the HW core register. ++ */ + static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv) + { + u32 hwid = priv->hw->synopsys_uid; +@@ -1475,11 +1524,11 @@ static u32 stmmac_get_synopsys_id(struct + } + + /** +- * stmmac_selec_desc_mode: to select among: normal/alternate/extend descriptors ++ * stmmac_selec_desc_mode - to select among: normal/alternate/extend descriptors + * @priv: driver private structure + * Description: select the Enhanced/Alternate or Normal descriptors. +- * In case of Enhanced/Alternate, it looks at the extended descriptors are +- * supported by the HW cap. register. ++ * In case of Enhanced/Alternate, it checks if the extended descriptors are ++ * supported by the HW capability register. + */ + static void stmmac_selec_desc_mode(struct stmmac_priv *priv) + { +@@ -1501,7 +1550,7 @@ static void stmmac_selec_desc_mode(struc + } + + /** +- * stmmac_get_hw_features: get MAC capabilities from the HW cap. register. ++ * stmmac_get_hw_features - get MAC capabilities from the HW cap. register. + * @priv: driver private structure + * Description: + * new GMAC chip generations have a new register to indicate the +@@ -1559,7 +1608,7 @@ static int stmmac_get_hw_features(struct + } + + /** +- * stmmac_check_ether_addr: check if the MAC addr is valid ++ * stmmac_check_ether_addr - check if the MAC addr is valid + * @priv: driver private structure + * Description: + * it is to verify if the MAC address is valid, in case of failures it +@@ -1578,7 +1627,7 @@ static void stmmac_check_ether_addr(stru + } + + /** +- * stmmac_init_dma_engine: DMA init. ++ * stmmac_init_dma_engine - DMA init. + * @priv: driver private structure + * Description: + * It inits the DMA invoking the specific MAC/GMAC callback. +@@ -1607,7 +1656,7 @@ static int stmmac_init_dma_engine(struct + } + + /** +- * stmmac_tx_timer: mitigation sw timer for tx. ++ * stmmac_tx_timer - mitigation sw timer for tx. + * @data: data pointer + * Description: + * This is the timer handler to directly invoke the stmmac_tx_clean. +@@ -1620,7 +1669,7 @@ static void stmmac_tx_timer(unsigned lon + } + + /** +- * stmmac_init_tx_coalesce: init tx mitigation options. ++ * stmmac_init_tx_coalesce - init tx mitigation options. + * @priv: driver private structure + * Description: + * This inits the transmit coalesce parameters: i.e. timer rate, +@@ -1639,15 +1688,18 @@ static void stmmac_init_tx_coalesce(stru + } + + /** +- * stmmac_hw_setup: setup mac in a usable state. ++ * stmmac_hw_setup - setup mac in a usable state. + * @dev : pointer to the device structure. + * Description: +- * This function sets up the ip in a usable state. ++ * this is the main function to setup the HW in a usable state because the ++ * dma engine is reset, the core registers are configured (e.g. AXI, ++ * Checksum features, timers). The DMA is ready to start receiving and ++ * transmitting. + * Return value: + * 0 on success and an appropriate (-)ve integer as defined in errno.h + * file on failure. + */ +-static int stmmac_hw_setup(struct net_device *dev) ++static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) + { + struct stmmac_priv *priv = netdev_priv(dev); + int ret; +@@ -1684,11 +1736,13 @@ static int stmmac_hw_setup(struct net_de + + stmmac_mmc_setup(priv); + +- ret = stmmac_init_ptp(priv); +- if (ret && ret != -EOPNOTSUPP) +- pr_warn("%s: failed PTP initialisation\n", __func__); ++ if (init_ptp) { ++ ret = stmmac_init_ptp(priv); ++ if (ret && ret != -EOPNOTSUPP) ++ pr_warn("%s: failed PTP initialisation\n", __func__); ++ } + +-#ifdef CONFIG_STMMAC_DEBUG_FS ++#ifdef CONFIG_DEBUG_FS + ret = stmmac_init_fs(dev); + if (ret < 0) + pr_warn("%s: failed debugFS registration\n", __func__); +@@ -1763,7 +1817,7 @@ static int stmmac_open(struct net_device + goto init_error; + } + +- ret = stmmac_hw_setup(dev); ++ ret = stmmac_hw_setup(dev, true); + if (ret < 0) { + pr_err("%s: Hw setup failed\n", __func__); + goto init_error; +@@ -1870,8 +1924,8 @@ static int stmmac_release(struct net_dev + + netif_carrier_off(dev); + +-#ifdef CONFIG_STMMAC_DEBUG_FS +- stmmac_exit_fs(); ++#ifdef CONFIG_DEBUG_FS ++ stmmac_exit_fs(dev); + #endif + + stmmac_release_ptp(priv); +@@ -1880,7 +1934,7 @@ static int stmmac_release(struct net_dev + } + + /** +- * stmmac_xmit: Tx entry point of the driver ++ * stmmac_xmit - Tx entry point of the driver + * @skb : the socket buffer + * @dev : device pointer + * Description : this is the tx entry point of the driver. +@@ -2024,6 +2078,7 @@ static netdev_tx_t stmmac_xmit(struct sk + if (!priv->hwts_tx_en) + skb_tx_timestamp(skb); + ++ netdev_sent_queue(dev, skb->len); + priv->hw->dma->enable_dma_transmission(priv->ioaddr); + + spin_unlock(&priv->tx_lock); +@@ -2055,7 +2110,7 @@ static void stmmac_rx_vlan(struct net_de + + + /** +- * stmmac_rx_refill: refill used skb preallocated buffers ++ * stmmac_rx_refill - refill used skb preallocated buffers + * @priv: driver private structure + * Description : this is to reallocate the skb for the reception process + * that is based on zero-copy. +@@ -2106,7 +2161,7 @@ static inline void stmmac_rx_refill(stru + } + + /** +- * stmmac_rx_refill: refill used skb preallocated buffers ++ * stmmac_rx - manage the receive process + * @priv: driver private structure + * @limit: napi bugget. + * Description : this the function called by the napi poll method. +@@ -2375,8 +2430,11 @@ static int stmmac_set_features(struct ne + * @irq: interrupt number. + * @dev_id: to pass the net device pointer. + * Description: this is the main driver interrupt service routine. +- * It calls the DMA ISR and also the core ISR to manage PMT, MMC, LPI +- * interrupts. ++ * It can call: ++ * o DMA service routine (to manage incoming frame reception and transmission ++ * status) ++ * o Core interrupts to manage: remote wake-up, management counter, LPI ++ * interrupts. + */ + static irqreturn_t stmmac_interrupt(int irq, void *dev_id) + { +@@ -2457,10 +2515,8 @@ static int stmmac_ioctl(struct net_devic + return ret; + } + +-#ifdef CONFIG_STMMAC_DEBUG_FS ++#ifdef CONFIG_DEBUG_FS + static struct dentry *stmmac_fs_dir; +-static struct dentry *stmmac_rings_status; +-static struct dentry *stmmac_dma_cap; + + static void sysfs_display_ring(void *head, int size, int extend_desc, + struct seq_file *seq) +@@ -2599,36 +2655,39 @@ static const struct file_operations stmm + + static int stmmac_init_fs(struct net_device *dev) + { +- /* Create debugfs entries */ +- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL); ++ struct stmmac_priv *priv = netdev_priv(dev); + +- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) { +- pr_err("ERROR %s, debugfs create directory failed\n", +- STMMAC_RESOURCE_NAME); ++ /* Create per netdev entries */ ++ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir); ++ ++ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) { ++ pr_err("ERROR %s/%s, debugfs create directory failed\n", ++ STMMAC_RESOURCE_NAME, dev->name); + + return -ENOMEM; + } + + /* Entry to report DMA RX/TX rings */ +- stmmac_rings_status = debugfs_create_file("descriptors_status", +- S_IRUGO, stmmac_fs_dir, dev, +- &stmmac_rings_status_fops); ++ priv->dbgfs_rings_status = ++ debugfs_create_file("descriptors_status", S_IRUGO, ++ priv->dbgfs_dir, dev, ++ &stmmac_rings_status_fops); + +- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) { ++ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) { + pr_info("ERROR creating stmmac ring debugfs file\n"); +- debugfs_remove(stmmac_fs_dir); ++ debugfs_remove_recursive(priv->dbgfs_dir); + + return -ENOMEM; + } + + /* Entry to report the DMA HW features */ +- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir, +- dev, &stmmac_dma_cap_fops); ++ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, ++ priv->dbgfs_dir, ++ dev, &stmmac_dma_cap_fops); + +- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) { ++ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) { + pr_info("ERROR creating stmmac MMC debugfs file\n"); +- debugfs_remove(stmmac_rings_status); +- debugfs_remove(stmmac_fs_dir); ++ debugfs_remove_recursive(priv->dbgfs_dir); + + return -ENOMEM; + } +@@ -2636,13 +2695,13 @@ static int stmmac_init_fs(struct net_dev + return 0; + } + +-static void stmmac_exit_fs(void) ++static void stmmac_exit_fs(struct net_device *dev) + { +- debugfs_remove(stmmac_rings_status); +- debugfs_remove(stmmac_dma_cap); +- debugfs_remove(stmmac_fs_dir); ++ struct stmmac_priv *priv = netdev_priv(dev); ++ ++ debugfs_remove_recursive(priv->dbgfs_dir); + } +-#endif /* CONFIG_STMMAC_DEBUG_FS */ ++#endif /* CONFIG_DEBUG_FS */ + + static const struct net_device_ops stmmac_netdev_ops = { + .ndo_open = stmmac_open, +@@ -2663,11 +2722,10 @@ static const struct net_device_ops stmma + /** + * stmmac_hw_init - Init the MAC device + * @priv: driver private structure +- * Description: this function detects which MAC device +- * (GMAC/MAC10-100) has to attached, checks the HW capability +- * (if supported) and sets the driver's features (for example +- * to use the ring or chaine mode or support the normal/enh +- * descriptor structure). ++ * Description: this function is to configure the MAC device according to ++ * some platform parameters or the HW capability register. It prepares the ++ * driver to use either ring or chain modes and to setup either enhanced or ++ * normal descriptors. + */ + static int stmmac_hw_init(struct stmmac_priv *priv) + { +@@ -2714,7 +2772,11 @@ static int stmmac_hw_init(struct stmmac_ + priv->plat->enh_desc = priv->dma_cap.enh_desc; + priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; + +- priv->plat->tx_coe = priv->dma_cap.tx_coe; ++ /* TXCOE doesn't work in thresh DMA mode */ ++ if (priv->plat->force_thresh_dma_mode) ++ priv->plat->tx_coe = 0; ++ else ++ priv->plat->tx_coe = priv->dma_cap.tx_coe; + + if (priv->dma_cap.rx_coe_type2) + priv->plat->rx_coe = STMMAC_RX_COE_TYPE2; +@@ -2747,13 +2809,15 @@ static int stmmac_hw_init(struct stmmac_ + * stmmac_dvr_probe + * @device: device pointer + * @plat_dat: platform data pointer +- * @addr: iobase memory address ++ * @res: stmmac resource pointer + * Description: this is the main probe function used to + * call the alloc_etherdev, allocate the priv structure. ++ * Return: ++ * returns 0 on success, otherwise errno. + */ +-struct stmmac_priv *stmmac_dvr_probe(struct device *device, +- struct plat_stmmacenet_data *plat_dat, +- void __iomem *addr) ++int stmmac_dvr_probe(struct device *device, ++ struct plat_stmmacenet_data *plat_dat, ++ struct stmmac_resources *res) + { + int ret = 0; + struct net_device *ndev = NULL; +@@ -2761,7 +2825,7 @@ struct stmmac_priv *stmmac_dvr_probe(str + + ndev = alloc_etherdev(sizeof(struct stmmac_priv)); + if (!ndev) +- return NULL; ++ return -ENOMEM; + + SET_NETDEV_DEV(ndev, device); + +@@ -2772,8 +2836,17 @@ struct stmmac_priv *stmmac_dvr_probe(str + stmmac_set_ethtool_ops(ndev); + priv->pause = pause; + priv->plat = plat_dat; +- priv->ioaddr = addr; +- priv->dev->base_addr = (unsigned long)addr; ++ priv->ioaddr = res->addr; ++ priv->dev->base_addr = (unsigned long)res->addr; ++ ++ priv->dev->irq = res->irq; ++ priv->wol_irq = res->wol_irq; ++ priv->lpi_irq = res->lpi_irq; ++ ++ if (res->mac) ++ memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN); ++ ++ dev_set_drvdata(device, priv->dev); + + /* Verify driver arguments */ + stmmac_verify_args(); +@@ -2800,6 +2873,16 @@ struct stmmac_priv *stmmac_dvr_probe(str + } + clk_prepare_enable(priv->stmmac_clk); + ++ priv->pclk = devm_clk_get(priv->device, "pclk"); ++ if (IS_ERR(priv->pclk)) { ++ if (PTR_ERR(priv->pclk) == -EPROBE_DEFER) { ++ ret = -EPROBE_DEFER; ++ goto error_pclk_get; ++ } ++ priv->pclk = NULL; ++ } ++ clk_prepare_enable(priv->pclk); ++ + priv->stmmac_rst = devm_reset_control_get(priv->device, + STMMAC_RESOURCE_NAME); + if (IS_ERR(priv->stmmac_rst)) { +@@ -2878,19 +2961,22 @@ struct stmmac_priv *stmmac_dvr_probe(str + } + } + +- return priv; ++ return 0; + + error_mdio_register: + unregister_netdev(ndev); + error_netdev_register: + netif_napi_del(&priv->napi); + error_hw_init: ++ clk_disable_unprepare(priv->pclk); ++error_pclk_get: + clk_disable_unprepare(priv->stmmac_clk); + error_clk_get: + free_netdev(ndev); + +- return ERR_PTR(ret); ++ return ret; + } ++EXPORT_SYMBOL_GPL(stmmac_dvr_probe); + + /** + * stmmac_dvr_remove +@@ -2908,20 +2994,28 @@ int stmmac_dvr_remove(struct net_device + priv->hw->dma->stop_tx(priv->ioaddr); + + stmmac_set_mac(priv->ioaddr, false); +- if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && +- priv->pcs != STMMAC_PCS_RTBI) +- stmmac_mdio_unregister(ndev); + netif_carrier_off(ndev); + unregister_netdev(ndev); + if (priv->stmmac_rst) + reset_control_assert(priv->stmmac_rst); ++ clk_disable_unprepare(priv->pclk); + clk_disable_unprepare(priv->stmmac_clk); ++ if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && ++ priv->pcs != STMMAC_PCS_RTBI) ++ stmmac_mdio_unregister(ndev); + free_netdev(ndev); + + return 0; + } ++EXPORT_SYMBOL_GPL(stmmac_dvr_remove); + +-#ifdef CONFIG_PM ++/** ++ * stmmac_suspend - suspend callback ++ * @ndev: net device pointer ++ * Description: this is the function to suspend the device and it is called ++ * by the platform driver to stop the network queue, release the resources, ++ * program the PMT register (for WoL), clean and release driver resources. ++ */ + int stmmac_suspend(struct net_device *ndev) + { + struct stmmac_priv *priv = netdev_priv(ndev); +@@ -2954,6 +3048,7 @@ int stmmac_suspend(struct net_device *nd + stmmac_set_mac(priv->ioaddr, false); + pinctrl_pm_select_sleep_state(priv->device); + /* Disable clock in case of PWM is off */ ++ clk_disable(priv->pclk); + clk_disable(priv->stmmac_clk); + } + spin_unlock_irqrestore(&priv->lock, flags); +@@ -2963,7 +3058,14 @@ int stmmac_suspend(struct net_device *nd + priv->oldduplex = -1; + return 0; + } ++EXPORT_SYMBOL_GPL(stmmac_suspend); + ++/** ++ * stmmac_resume - resume callback ++ * @ndev: net device pointer ++ * Description: when resume this function is invoked to setup the DMA and CORE ++ * in a usable state. ++ */ + int stmmac_resume(struct net_device *ndev) + { + struct stmmac_priv *priv = netdev_priv(ndev); +@@ -2987,6 +3089,7 @@ int stmmac_resume(struct net_device *nde + pinctrl_pm_select_default_state(priv->device); + /* enable the clk prevously disabled */ + clk_enable(priv->stmmac_clk); ++ clk_enable(priv->pclk); + /* reset the phy so that it's ready */ + if (priv->mii) + stmmac_mdio_reset(priv->mii); +@@ -2995,7 +3098,7 @@ int stmmac_resume(struct net_device *nde + netif_device_attach(ndev); + + init_dma_desc_rings(ndev, GFP_ATOMIC); +- stmmac_hw_setup(ndev); ++ stmmac_hw_setup(ndev, false); + stmmac_init_tx_coalesce(priv); + + napi_enable(&priv->napi); +@@ -3009,37 +3112,7 @@ int stmmac_resume(struct net_device *nde + + return 0; + } +-#endif /* CONFIG_PM */ +- +-/* Driver can be configured w/ and w/ both PCI and Platf drivers +- * depending on the configuration selected. +- */ +-static int __init stmmac_init(void) +-{ +- int ret; +- +- ret = stmmac_register_platform(); +- if (ret) +- goto err; +- ret = stmmac_register_pci(); +- if (ret) +- goto err_pci; +- return 0; +-err_pci: +- stmmac_unregister_platform(); +-err: +- pr_err("stmmac: driver registration failed\n"); +- return ret; +-} +- +-static void __exit stmmac_exit(void) +-{ +- stmmac_unregister_platform(); +- stmmac_unregister_pci(); +-} +- +-module_init(stmmac_init); +-module_exit(stmmac_exit); ++EXPORT_SYMBOL_GPL(stmmac_resume); + + #ifndef MODULE + static int __init stmmac_cmdline_opt(char *str) +@@ -3094,6 +3167,35 @@ err: + __setup("stmmaceth=", stmmac_cmdline_opt); + #endif /* MODULE */ + ++static int __init stmmac_init(void) ++{ ++#ifdef CONFIG_DEBUG_FS ++ /* Create debugfs main directory if it doesn't exist yet */ ++ if (!stmmac_fs_dir) { ++ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL); ++ ++ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) { ++ pr_err("ERROR %s, debugfs create directory failed\n", ++ STMMAC_RESOURCE_NAME); ++ ++ return -ENOMEM; ++ } ++ } ++#endif ++ ++ return 0; ++} ++ ++static void __exit stmmac_exit(void) ++{ ++#ifdef CONFIG_DEBUG_FS ++ debugfs_remove_recursive(stmmac_fs_dir); ++#endif ++} ++ ++module_init(stmmac_init) ++module_exit(stmmac_exit) ++ + MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver"); + MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); + MODULE_LICENSE("GPL"); +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +@@ -161,11 +161,16 @@ int stmmac_mdio_reset(struct mii_bus *bu + + if (!gpio_request(reset_gpio, "mdio-reset")) { + gpio_direction_output(reset_gpio, active_low ? 1 : 0); +- udelay(data->delays[0]); ++ if (data->delays[0]) ++ msleep(DIV_ROUND_UP(data->delays[0], 1000)); ++ + gpio_set_value(reset_gpio, active_low ? 0 : 1); +- udelay(data->delays[1]); ++ if (data->delays[1]) ++ msleep(DIV_ROUND_UP(data->delays[1], 1000)); ++ + gpio_set_value(reset_gpio, active_low ? 1 : 0); +- udelay(data->delays[2]); ++ if (data->delays[2]) ++ msleep(DIV_ROUND_UP(data->delays[2], 1000)); + } + } + #endif +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +@@ -24,38 +24,128 @@ + *******************************************************************************/ + + #include <linux/pci.h> ++#include <linux/dmi.h> ++ + #include "stmmac.h" + +-static struct plat_stmmacenet_data plat_dat; +-static struct stmmac_mdio_bus_data mdio_data; +-static struct stmmac_dma_cfg dma_cfg; +- +-static void stmmac_default_data(void) +-{ +- memset(&plat_dat, 0, sizeof(struct plat_stmmacenet_data)); +- +- plat_dat.bus_id = 1; +- plat_dat.phy_addr = 0; +- plat_dat.interface = PHY_INTERFACE_MODE_GMII; +- plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ +- plat_dat.has_gmac = 1; +- plat_dat.force_sf_dma_mode = 1; +- +- mdio_data.phy_reset = NULL; +- mdio_data.phy_mask = 0; +- plat_dat.mdio_bus_data = &mdio_data; +- +- dma_cfg.pbl = 32; +- dma_cfg.burst_len = DMA_AXI_BLEN_256; +- plat_dat.dma_cfg = &dma_cfg; ++/* ++ * This struct is used to associate PCI Function of MAC controller on a board, ++ * discovered via DMI, with the address of PHY connected to the MAC. The ++ * negative value of the address means that MAC controller is not connected ++ * with PHY. ++ */ ++struct stmmac_pci_dmi_data { ++ const char *name; ++ 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; ++}; ++ ++static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info) ++{ ++ const char *name = dmi_get_system_info(DMI_BOARD_NAME); ++ 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; ++ ++ for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) { ++ if (!strcmp(dmi->name, name) && dmi->func == func) ++ return dmi->phy_addr; ++ } ++ ++ return -ENODEV; ++} ++ ++static void stmmac_default_data(struct plat_stmmacenet_data *plat) ++{ ++ plat->bus_id = 1; ++ plat->phy_addr = 0; ++ plat->interface = PHY_INTERFACE_MODE_GMII; ++ plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ ++ plat->has_gmac = 1; ++ plat->force_sf_dma_mode = 1; ++ ++ plat->mdio_bus_data->phy_reset = NULL; ++ plat->mdio_bus_data->phy_mask = 0; ++ ++ plat->dma_cfg->pbl = 32; ++ plat->dma_cfg->burst_len = DMA_AXI_BLEN_256; ++ ++ /* Set default value for multicast hash bins */ ++ plat->multicast_filter_bins = HASH_TABLE_SIZE; ++ ++ /* Set default value for unicast filter entries */ ++ plat->unicast_filter_entries = 1; ++} ++ ++static int quark_default_data(struct plat_stmmacenet_data *plat, ++ struct stmmac_pci_info *info) ++{ ++ struct pci_dev *pdev = info->pdev; ++ int ret; ++ ++ /* ++ * 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; ++ ++ plat->bus_id = PCI_DEVID(pdev->bus->number, pdev->devfn); ++ plat->phy_addr = ret; ++ plat->interface = PHY_INTERFACE_MODE_RMII; ++ plat->clk_csr = 2; ++ plat->has_gmac = 1; ++ plat->force_sf_dma_mode = 1; ++ ++ plat->mdio_bus_data->phy_reset = NULL; ++ plat->mdio_bus_data->phy_mask = 0; ++ ++ plat->dma_cfg->pbl = 16; ++ plat->dma_cfg->burst_len = DMA_AXI_BLEN_256; ++ plat->dma_cfg->fixed_burst = 1; + + /* Set default value for multicast hash bins */ +- plat_dat.multicast_filter_bins = HASH_TABLE_SIZE; ++ plat->multicast_filter_bins = HASH_TABLE_SIZE; + + /* Set default value for unicast filter entries */ +- plat_dat.unicast_filter_entries = 1; ++ plat->unicast_filter_entries = 1; ++ ++ 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, ++ }, ++ {} ++}; ++ ++static struct stmmac_pci_info quark_pci_info = { ++ .setup = quark_default_data, ++ .dmi = quark_pci_dmi_data, ++}; ++ + /** + * stmmac_pci_probe + * +@@ -71,64 +161,65 @@ static void stmmac_default_data(void) + static int stmmac_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) + { +- int ret = 0; +- void __iomem *addr = NULL; +- struct stmmac_priv *priv = NULL; ++ struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; ++ struct plat_stmmacenet_data *plat; ++ struct stmmac_resources res; + int i; ++ int ret; ++ ++ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); ++ if (!plat) ++ return -ENOMEM; ++ ++ plat->mdio_bus_data = devm_kzalloc(&pdev->dev, ++ sizeof(*plat->mdio_bus_data), ++ GFP_KERNEL); ++ if (!plat->mdio_bus_data) ++ return -ENOMEM; ++ ++ plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), ++ GFP_KERNEL); ++ if (!plat->dma_cfg) ++ return -ENOMEM; + + /* Enable pci device */ +- ret = pci_enable_device(pdev); ++ ret = pcim_enable_device(pdev); + if (ret) { +- pr_err("%s : ERROR: failed to enable %s device\n", __func__, +- pci_name(pdev)); ++ dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", ++ __func__); + return ret; + } +- if (pci_request_regions(pdev, STMMAC_RESOURCE_NAME)) { +- pr_err("%s: ERROR: failed to get PCI region\n", __func__); +- ret = -ENODEV; +- goto err_out_req_reg_failed; +- } + + /* Get the base address of device */ +- for (i = 0; i <= 5; i++) { ++ for (i = 0; i <= PCI_STD_RESOURCE_END; i++) { + if (pci_resource_len(pdev, i) == 0) + continue; +- addr = pci_iomap(pdev, i, 0); +- if (addr == NULL) { +- pr_err("%s: ERROR: cannot map register memory aborting", +- __func__); +- ret = -EIO; +- goto err_out_map_failed; +- } ++ ret = pcim_iomap_regions(pdev, BIT(i), pci_name(pdev)); ++ if (ret) ++ return ret; + break; + } +- pci_set_master(pdev); +- +- stmmac_default_data(); + +- priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr); +- if (IS_ERR(priv)) { +- pr_err("%s: main driver probe failed", __func__); +- ret = PTR_ERR(priv); +- goto err_out; +- } +- priv->dev->irq = pdev->irq; +- priv->wol_irq = pdev->irq; +- +- pci_set_drvdata(pdev, priv->dev); ++ pci_set_master(pdev); + +- pr_debug("STMMAC platform driver registration completed"); ++ if (info) { ++ info->pdev = pdev; ++ if (info->setup) { ++ ret = info->setup(plat, info); ++ if (ret) ++ return ret; ++ } ++ } else ++ stmmac_default_data(plat); + +- return 0; ++ pci_enable_msi(pdev); + +-err_out: +- pci_clear_master(pdev); +-err_out_map_failed: +- pci_release_regions(pdev); +-err_out_req_reg_failed: +- pci_disable_device(pdev); ++ memset(&res, 0, sizeof(res)); ++ res.addr = pcim_iomap_table(pdev)[i]; ++ res.wol_irq = pdev->irq; ++ res.irq = pdev->irq; + +- return ret; ++ return stmmac_dvr_probe(&pdev->dev, plat, &res); + } + + /** +@@ -141,61 +232,55 @@ err_out_req_reg_failed: + static void stmmac_pci_remove(struct pci_dev *pdev) + { + struct net_device *ndev = pci_get_drvdata(pdev); +- struct stmmac_priv *priv = netdev_priv(ndev); + + stmmac_dvr_remove(ndev); +- +- pci_iounmap(pdev, priv->ioaddr); +- pci_release_regions(pdev); +- pci_disable_device(pdev); + } + +-#ifdef CONFIG_PM +-static int stmmac_pci_suspend(struct pci_dev *pdev, pm_message_t state) ++#ifdef CONFIG_PM_SLEEP ++static int stmmac_pci_suspend(struct device *dev) + { ++ struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *ndev = pci_get_drvdata(pdev); +- int ret; +- +- ret = stmmac_suspend(ndev); +- pci_save_state(pdev); +- pci_set_power_state(pdev, pci_choose_state(pdev, state)); + +- return ret; ++ return stmmac_suspend(ndev); + } + +-static int stmmac_pci_resume(struct pci_dev *pdev) ++static int stmmac_pci_resume(struct device *dev) + { ++ struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *ndev = pci_get_drvdata(pdev); + +- pci_set_power_state(pdev, PCI_D0); +- pci_restore_state(pdev); +- + return stmmac_resume(ndev); + } + #endif + ++static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume); ++ + #define STMMAC_VENDOR_ID 0x700 ++#define STMMAC_QUARK_ID 0x0937 + #define STMMAC_DEVICE_ID 0x1108 + + 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}, + {} + }; + + MODULE_DEVICE_TABLE(pci, stmmac_id_table); + +-struct pci_driver stmmac_pci_driver = { ++static struct pci_driver stmmac_pci_driver = { + .name = STMMAC_RESOURCE_NAME, + .id_table = stmmac_id_table, + .probe = stmmac_pci_probe, + .remove = stmmac_pci_remove, +-#ifdef CONFIG_PM +- .suspend = stmmac_pci_suspend, +- .resume = stmmac_pci_resume, +-#endif ++ .driver = { ++ .pm = &stmmac_pm_ops, ++ }, + }; + ++module_pci_driver(stmmac_pci_driver); ++ + MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PCI driver"); + MODULE_AUTHOR("Rayagond Kokatanur <rayagond.kokatanur@vayavyalabs.com>"); + MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -23,41 +23,23 @@ + *******************************************************************************/ + + #include <linux/platform_device.h> ++#include <linux/module.h> + #include <linux/io.h> + #include <linux/of.h> + #include <linux/of_net.h> + #include <linux/of_device.h> +-#include "stmmac.h" ++#include <linux/of_mdio.h> + +-static const struct of_device_id stmmac_dt_ids[] = { +-#ifdef CONFIG_DWMAC_MESON +- { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data}, +-#endif +-#ifdef CONFIG_DWMAC_SUNXI +- { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data}, +-#endif +-#ifdef CONFIG_DWMAC_STI +- { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, +- { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, +- { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, +- { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, +-#endif +-#ifdef CONFIG_DWMAC_SOCFPGA +- { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data }, +-#endif +- /* SoC specific glue layers should come before generic bindings */ +- { .compatible = "st,spear600-gmac"}, +- { .compatible = "snps,dwmac-3.610"}, +- { .compatible = "snps,dwmac-3.70a"}, +- { .compatible = "snps,dwmac-3.710"}, +- { .compatible = "snps,dwmac"}, +- { /* sentinel */ } +-}; +-MODULE_DEVICE_TABLE(of, stmmac_dt_ids); ++#include "stmmac.h" ++#include "stmmac_platform.h" + + #ifdef CONFIG_OF + +-/* This function validates the number of Multicast filtering bins specified ++/** ++ * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins ++ * @mcast_bins: Multicast filtering bins ++ * Description: ++ * this function validates the number of Multicast filtering bins specified + * by the configuration through the device tree. The Synopsys GMAC supports + * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC + * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds +@@ -83,7 +65,11 @@ static int dwmac1000_validate_mcast_bins + return x; + } + +-/* This function validates the number of Unicast address entries supported ++/** ++ * dwmac1000_validate_ucast_entries - validate the Unicast address entries ++ * @ucast_entries: number of Unicast address entries ++ * Description: ++ * This function validates the number of Unicast address entries supported + * by a particular Synopsys 10/100/1000 controller. The Synopsys controller + * supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter + * logic. This function validates a valid, supported configuration is +@@ -109,37 +95,25 @@ static int dwmac1000_validate_ucast_entr + return x; + } + +-static int stmmac_probe_config_dt(struct platform_device *pdev, +- struct plat_stmmacenet_data *plat, +- const char **mac) ++/** ++ * stmmac_probe_config_dt - parse device-tree driver parameters ++ * @pdev: platform_device structure ++ * @plat: driver data platform structure ++ * @mac: MAC address to use ++ * Description: ++ * this function is to read the driver parameters from device-tree and ++ * set some private fields that will be used by the main at runtime. ++ */ ++struct plat_stmmacenet_data * ++stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + { + struct device_node *np = pdev->dev.of_node; ++ struct plat_stmmacenet_data *plat; + struct stmmac_dma_cfg *dma_cfg; +- const struct of_device_id *device; + +- if (!np) +- return -ENODEV; +- +- device = of_match_device(stmmac_dt_ids, &pdev->dev); +- if (!device) +- return -ENODEV; +- +- if (device->data) { +- const struct stmmac_of_data *data = device->data; +- plat->has_gmac = data->has_gmac; +- plat->enh_desc = data->enh_desc; +- plat->tx_coe = data->tx_coe; +- plat->rx_coe = data->rx_coe; +- plat->bugged_jumbo = data->bugged_jumbo; +- plat->pmt = data->pmt; +- plat->riwt_off = data->riwt_off; +- plat->fix_mac_speed = data->fix_mac_speed; +- plat->bus_setup = data->bus_setup; +- plat->setup = data->setup; +- plat->free = data->free; +- plat->init = data->init; +- plat->exit = data->exit; +- } ++ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); ++ if (!plat) ++ return ERR_PTR(-ENOMEM); + + *mac = of_get_mac_address(np); + plat->interface = of_get_phy_mode(np); +@@ -155,13 +129,24 @@ static int stmmac_probe_config_dt(struct + /* Default to phy auto-detection */ + plat->phy_addr = -1; + ++ /* If we find a phy-handle property, use it as the PHY */ ++ plat->phy_node = of_parse_phandle(np, "phy-handle", 0); ++ ++ /* If phy-handle is not specified, check if we have a fixed-phy */ ++ if (!plat->phy_node && of_phy_is_fixed_link(np)) { ++ if ((of_phy_register_fixed_link(np) < 0)) ++ return ERR_PTR(-ENODEV); ++ ++ plat->phy_node = of_node_get(np); ++ } ++ + /* "snps,phy-addr" is not a standard property. Mark it as deprecated + * and warn of its use. Remove this when phy node support is added. + */ + if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) + dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); + +- if (plat->phy_bus_name) ++ if (plat->phy_node || plat->phy_bus_name) + plat->mdio_bus_data = NULL; + else + plat->mdio_bus_data = +@@ -169,6 +154,10 @@ static int stmmac_probe_config_dt(struct + sizeof(struct stmmac_mdio_bus_data), + GFP_KERNEL); + ++ of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size); ++ ++ of_property_read_u32(np, "rx-fifo-depth", &plat->rx_fifo_size); ++ + plat->force_sf_dma_mode = + of_property_read_bool(np, "snps,force_sf_dma_mode"); + +@@ -177,6 +166,12 @@ static int stmmac_probe_config_dt(struct + */ + plat->maxmtu = JUMBO_LEN; + ++ /* Set default value for multicast hash bins */ ++ plat->multicast_filter_bins = HASH_TABLE_SIZE; ++ ++ /* Set default value for unicast filter entries */ ++ plat->unicast_filter_entries = 1; ++ + /* + * Currently only the properties needed on SPEAr600 + * are provided. All other properties should be added +@@ -215,14 +210,19 @@ static int stmmac_probe_config_dt(struct + if (of_find_property(np, "snps,pbl", NULL)) { + dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), + GFP_KERNEL); +- if (!dma_cfg) +- return -ENOMEM; ++ if (!dma_cfg) { ++ of_node_put(np); ++ return ERR_PTR(-ENOMEM); ++ } + plat->dma_cfg = dma_cfg; + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); + dma_cfg->fixed_burst = + of_property_read_bool(np, "snps,fixed-burst"); + dma_cfg->mixed_burst = + of_property_read_bool(np, "snps,mixed-burst"); ++ of_property_read_u32(np, "snps,burst_len", &dma_cfg->burst_len); ++ if (dma_cfg->burst_len < 0 || dma_cfg->burst_len > 256) ++ dma_cfg->burst_len = 0; + } + plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode"); + if (plat->force_thresh_dma_mode) { +@@ -230,123 +230,60 @@ static int stmmac_probe_config_dt(struct + pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set."); + } + +- return 0; ++ return plat; + } + #else +-static int stmmac_probe_config_dt(struct platform_device *pdev, +- struct plat_stmmacenet_data *plat, +- const char **mac) ++struct plat_stmmacenet_data * ++stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + { +- return -ENOSYS; ++ return ERR_PTR(-ENOSYS); + } + #endif /* CONFIG_OF */ ++EXPORT_SYMBOL_GPL(stmmac_probe_config_dt); + +-/** +- * stmmac_pltfr_probe +- * @pdev: platform device pointer +- * Description: platform_device probe function. It allocates +- * the necessary resources and invokes the main to init +- * the net device, register the mdio bus etc. +- */ +-static int stmmac_pltfr_probe(struct platform_device *pdev) ++int stmmac_get_platform_resources(struct platform_device *pdev, ++ struct stmmac_resources *stmmac_res) + { +- int ret = 0; + struct resource *res; +- struct device *dev = &pdev->dev; +- void __iomem *addr = NULL; +- struct stmmac_priv *priv = NULL; +- struct plat_stmmacenet_data *plat_dat = NULL; +- const char *mac = NULL; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- addr = devm_ioremap_resource(dev, res); +- if (IS_ERR(addr)) +- return PTR_ERR(addr); +- +- plat_dat = dev_get_platdata(&pdev->dev); +- +- if (!plat_dat) +- plat_dat = devm_kzalloc(&pdev->dev, +- sizeof(struct plat_stmmacenet_data), +- GFP_KERNEL); +- if (!plat_dat) { +- pr_err("%s: ERROR: no memory", __func__); +- return -ENOMEM; +- } +- +- /* Set default value for multicast hash bins */ +- plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; +- +- /* Set default value for unicast filter entries */ +- plat_dat->unicast_filter_entries = 1; +- +- if (pdev->dev.of_node) { +- ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); +- if (ret) { +- pr_err("%s: main dt probe failed", __func__); +- return ret; +- } +- } +- +- /* Custom setup (if needed) */ +- if (plat_dat->setup) { +- plat_dat->bsp_priv = plat_dat->setup(pdev); +- if (IS_ERR(plat_dat->bsp_priv)) +- return PTR_ERR(plat_dat->bsp_priv); +- } +- +- /* Custom initialisation (if needed)*/ +- if (plat_dat->init) { +- ret = plat_dat->init(pdev, plat_dat->bsp_priv); +- if (unlikely(ret)) +- return ret; +- } + +- priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr); +- if (IS_ERR(priv)) { +- pr_err("%s: main driver probe failed", __func__); +- return PTR_ERR(priv); +- } ++ memset(stmmac_res, 0, sizeof(*stmmac_res)); + +- /* Get MAC address if available (DT) */ +- if (mac) +- memcpy(priv->dev->dev_addr, mac, ETH_ALEN); +- +- /* Get the MAC information */ +- priv->dev->irq = platform_get_irq_byname(pdev, "macirq"); +- if (priv->dev->irq < 0) { +- if (priv->dev->irq != -EPROBE_DEFER) { +- netdev_err(priv->dev, +- "MAC IRQ configuration information not found\n"); ++ /* Get IRQ information early to have an ability to ask for deferred ++ * probe if needed before we went too far with resource allocation. ++ */ ++ stmmac_res->irq = platform_get_irq_byname(pdev, "macirq"); ++ if (stmmac_res->irq < 0) { ++ if (stmmac_res->irq != -EPROBE_DEFER) { ++ dev_err(&pdev->dev, ++ "MAC IRQ configuration information not found\n"); + } +- return priv->dev->irq; ++ return stmmac_res->irq; + } + +- /* +- * On some platforms e.g. SPEAr the wake up irq differs from the mac irq ++ /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq + * The external wake up irq can be passed through the platform code + * named as "eth_wake_irq" + * + * In case the wake up interrupt is not passed from the platform + * so the driver will continue to use the mac irq (ndev->irq) + */ +- priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); +- if (priv->wol_irq < 0) { +- if (priv->wol_irq == -EPROBE_DEFER) ++ stmmac_res->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); ++ if (stmmac_res->wol_irq < 0) { ++ if (stmmac_res->wol_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; +- priv->wol_irq = priv->dev->irq; ++ stmmac_res->wol_irq = stmmac_res->irq; + } + +- priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); +- if (priv->lpi_irq == -EPROBE_DEFER) ++ stmmac_res->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); ++ if (stmmac_res->lpi_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + +- platform_set_drvdata(pdev, priv->dev); +- +- pr_debug("STMMAC platform driver registration completed"); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ stmmac_res->addr = devm_ioremap_resource(&pdev->dev, res); + +- return 0; ++ return PTR_ERR_OR_ZERO(stmmac_res->addr); + } ++EXPORT_SYMBOL_GPL(stmmac_get_platform_resources); + + /** + * stmmac_pltfr_remove +@@ -354,7 +291,7 @@ static int stmmac_pltfr_probe(struct pla + * Description: this function calls the main to free the net resources + * and calls the platforms hook and release the resources (e.g. mem). + */ +-static int stmmac_pltfr_remove(struct platform_device *pdev) ++int stmmac_pltfr_remove(struct platform_device *pdev) + { + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); +@@ -363,13 +300,18 @@ static int stmmac_pltfr_remove(struct pl + if (priv->plat->exit) + priv->plat->exit(pdev, priv->plat->bsp_priv); + +- if (priv->plat->free) +- priv->plat->free(pdev, priv->plat->bsp_priv); +- + return ret; + } ++EXPORT_SYMBOL_GPL(stmmac_pltfr_remove); + +-#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP ++/** ++ * stmmac_pltfr_suspend ++ * @dev: device pointer ++ * Description: this function is invoked when suspend the driver and it direcly ++ * call the main suspend function and then, if required, on some platform, it ++ * can call an exit helper. ++ */ + static int stmmac_pltfr_suspend(struct device *dev) + { + int ret; +@@ -384,6 +326,13 @@ static int stmmac_pltfr_suspend(struct d + return ret; + } + ++/** ++ * stmmac_pltfr_resume ++ * @dev: device pointer ++ * Description: this function is invoked when resume the driver before calling ++ * the main resume function, on some platforms, it can call own init helper ++ * if required. ++ */ + static int stmmac_pltfr_resume(struct device *dev) + { + struct net_device *ndev = dev_get_drvdata(dev); +@@ -395,23 +344,12 @@ static int stmmac_pltfr_resume(struct de + + return stmmac_resume(ndev); + } ++#endif /* CONFIG_PM_SLEEP */ + +-#endif /* CONFIG_PM */ +- +-static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, +- stmmac_pltfr_suspend, stmmac_pltfr_resume); +- +-struct platform_driver stmmac_pltfr_driver = { +- .probe = stmmac_pltfr_probe, +- .remove = stmmac_pltfr_remove, +- .driver = { +- .name = STMMAC_RESOURCE_NAME, +- .owner = THIS_MODULE, +- .pm = &stmmac_pltfr_pm_ops, +- .of_match_table = of_match_ptr(stmmac_dt_ids), +- }, +-}; ++SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, stmmac_pltfr_suspend, ++ stmmac_pltfr_resume); ++EXPORT_SYMBOL_GPL(stmmac_pltfr_pm_ops); + +-MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver"); ++MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet platform support"); + MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); + MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h +@@ -0,0 +1,33 @@ ++/******************************************************************************* ++ Copyright (C) 2007-2009 STMicroelectronics Ltd ++ ++ This program is free software; you can redistribute it and/or modify it ++ under the terms and conditions of the GNU General Public License, ++ version 2, as published by the Free Software Foundation. ++ ++ This program is distributed in the hope 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. ++ ++ The full GNU General Public License is included in this distribution in ++ the file called "COPYING". ++ ++ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> ++*******************************************************************************/ ++ ++#ifndef __STMMAC_PLATFORM_H__ ++#define __STMMAC_PLATFORM_H__ ++ ++#include "stmmac.h" ++ ++struct plat_stmmacenet_data * ++stmmac_probe_config_dt(struct platform_device *pdev, const char **mac); ++ ++int stmmac_get_platform_resources(struct platform_device *pdev, ++ struct stmmac_resources *stmmac_res); ++ ++int stmmac_pltfr_remove(struct platform_device *pdev); ++extern const struct dev_pm_ops stmmac_pltfr_pm_ops; ++ ++#endif /* __STMMAC_PLATFORM_H__ */ |