diff --git a/configs/thead_860_compat_next_glibc_br_defconfig b/configs/thead_860_compat_next_glibc_br_defconfig index 76bb39568d..7362eef863 100644 --- a/configs/thead_860_compat_next_glibc_br_defconfig +++ b/configs/thead_860_compat_next_glibc_br_defconfig @@ -7,6 +7,7 @@ BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_UIMAGE=y BR2_LINUX_KERNEL_CUSTOM_TARBALL=y BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="https://github.com/c-sky/csky-linux/archive/$(CSKY_LINUX_NEXT_VERSION).tar.gz" +BR2_LINUX_KERNEL_PATCH="package/linux-patch-thead/next/0017-drivers-net-Add-dwmac-thead-added.patch" BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="board/csky-ci/defconfigs/kernel.860.fragment board/csky-ci/defconfigs/kernel.pci-9pfs-gpu.fragment" diff --git a/package/linux-patch-thead/next/0017-drivers-net-Add-dwmac-thead-added.patch b/package/linux-patch-thead/next/0017-drivers-net-Add-dwmac-thead-added.patch new file mode 100644 index 0000000000..e0d643b6bd --- /dev/null +++ b/package/linux-patch-thead/next/0017-drivers-net-Add-dwmac-thead-added.patch @@ -0,0 +1,638 @@ +From c23145d4feef9e3ade7de7f25740a5fdc795dc3f Mon Sep 17 00:00:00 2001 +From: Lin Lei +Date: Thu, 30 Jul 2020 07:21:48 +0000 +Subject: [PATCH 17/65] drivers/net: Add dwmac-thead added. + +Signed-off-by: Guo Ren +--- + drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +- + .../net/ethernet/stmicro/stmmac/dwmac-thead.c | 606 ++++++++++++++++++ + 2 files changed, 607 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c + +diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile +index 24e6145d4eae..1ad3e4cee71e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_STMMAC_ETH) += stmmac.o ++obj-$(CONFIG_STMMAC_ETH) += stmmac.o dwmac-thead.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 \ +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c +new file mode 100644 +index 000000000000..5f14951b611f +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c +@@ -0,0 +1,606 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++#include ++ ++#include "stmmac_platform.h" ++ ++/* clock registers */ ++#define GMAC_CLK_CFG0 0x00 ++#define GMAC_CLK_CFG1 0x04 ++#define GMAC_CLK_CFG2 0x08 ++#define GMAC_CLK_CFG3 0x0C ++#define GMAC_CLK_CFG4 0x10 ++#define GMAC_CLK_CFG5 0x14 ++#define GMAC_CLK_CFG6 0x18 ++ ++/* phy interface */ ++#define DWMAC_PHYIF_MII_GMII 0 ++#define DWMAC_PHYIF_RGMII 1 ++#define DWMAC_PHYIF_RMII 4 ++/* register bit fields, bit[3]: reserved, bit[2:0]: phy interface */ ++#define DWMAC_PHYIF_MASK 0x7 ++#define DWMAC_PHYIF_BIT_WIDTH 4 ++ ++/* TXCLK direction, 1:input, 0:output */ ++#define TXCLK_DIR_OUTPUT 0 ++#define TXCLK_DIR_INPUT 1 ++ ++#define GMAC_CLK_PLLOUT_250M 250000000 ++#define GMAC_GMII_RGMII_RATE 125000000 ++#define GMAC_MII_RATE 25000000 ++/* clock divider for speed */ ++#define GMAC_CLKDIV_125M (GMAC_CLK_PLLOUT_250M / GMAC_GMII_RGMII_RATE) ++#define GMAC_CLKDIV_25M (GMAC_CLK_PLLOUT_250M / GMAC_MII_RATE) ++ ++struct thead_dwmac_priv_data { ++ int id; ++ void __iomem *phy_if_reg; ++ void __iomem *txclk_dir_reg; ++ void __iomem *gmac_clk_reg; ++ phy_interface_t interface; ++ struct clk *gmac_pll_clk; ++ unsigned int gmac_pll_clk_freq; ++}; ++ ++/* set GMAC PHY interface, 0:MII/GMII, 1:RGMII, 4:RMII */ ++static void thead_dwmac_set_phy_if(struct platform_device *pdev, ++ void __iomem *phy_if_reg, int interface, ++ int devid) ++{ ++ struct device *dev = &pdev->dev; ++ unsigned int phyif = PHY_INTERFACE_MODE_MII; ++ volatile uint32_t reg; ++ ++ if (phy_if_reg == NULL) ++ return; ++ ++ switch (interface) ++ { ++ case PHY_INTERFACE_MODE_MII: ++ case PHY_INTERFACE_MODE_GMII: ++ phyif = DWMAC_PHYIF_MII_GMII; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ phyif = DWMAC_PHYIF_RGMII; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ phyif = DWMAC_PHYIF_RMII; ++ break; ++ default: ++ dev_err(dev, "phy interface %d not supported\n", interface); ++ return; ++ }; ++ ++ reg = readl(phy_if_reg); ++ reg &= ~(DWMAC_PHYIF_MASK << (DWMAC_PHYIF_BIT_WIDTH * devid)); ++ reg |= (phyif & DWMAC_PHYIF_MASK) << (DWMAC_PHYIF_BIT_WIDTH * devid); ++ writel(reg, phy_if_reg); ++} ++ ++/* ++ * set GMAC TXCLK direction ++ * MII : TXCLK is input ++ * GMII/RGMII : TXCLK is output ++ */ ++static void thead_dwmac_set_txclk_dir(struct platform_device *pdev, ++ void __iomem *txclk_dir_reg, int interface) ++{ ++ struct device *dev = &pdev->dev; ++ unsigned int txclk_dir = TXCLK_DIR_INPUT; ++ ++ if (txclk_dir_reg == NULL) ++ return; ++ ++ switch (interface) ++ { ++ case PHY_INTERFACE_MODE_MII: ++ case PHY_INTERFACE_MODE_RMII: ++ txclk_dir = TXCLK_DIR_INPUT; ++ break; ++ case PHY_INTERFACE_MODE_GMII: ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ txclk_dir = TXCLK_DIR_OUTPUT; ++ break; ++ default: ++ dev_err(dev, "phy interface %d not supported\n", interface); ++ return; ++ }; ++ ++ writel(txclk_dir, txclk_dir_reg); ++} ++ ++static void thead_dwmac_set_clk_source(struct platform_device *pdev, ++ void __iomem *gmac_clk_reg, int interface) ++{ ++ struct device *dev = &pdev->dev; ++ volatile uint32_t reg; ++ ++ if (gmac_clk_reg == NULL) ++ return; ++ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG0); ++ ++ /* RX clock source */ ++ reg |= BIT(7); /* gmac_rx_clk_sel: extern pin */ ++ ++ /* TX clock source */ ++ if (interface == PHY_INTERFACE_MODE_MII) { ++ reg |= BIT(1); /* gmac_tx_clk_sel: extern pin */ ++ reg &= ~BIT(2); /* gmac_tx_clk_gbit_sel: u_tx_clk_mux */ ++ } else if (interface == PHY_INTERFACE_MODE_GMII) { ++ reg &= ~BIT(5); /* gmac_tx_clk_out_sel: GMAC PLL */ ++ reg |= BIT(2); /* gmac_tx_clk_gbit_sel: GMAC PLL */ ++ } else if (interface == PHY_INTERFACE_MODE_RGMII ++ || interface == PHY_INTERFACE_MODE_RGMII_ID ++ || interface == PHY_INTERFACE_MODE_RGMII_RXID ++ || interface == PHY_INTERFACE_MODE_RGMII_TXID) { ++ reg &= ~BIT(5); /* gmac_tx_clk_out_sel: GMAC PLL */ ++ reg |= BIT(2); /* gmac_tx_clk_gbit_sel: GMAC PLL */ ++ } else { ++ dev_err(dev, "phy interface %d not supported\n", interface); ++ return; ++ } ++ ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG0); ++} ++ ++ ++/* set clock source */ ++static void thead_dwmac_set_clock_delay(struct platform_device *pdev, ++ void __iomem *gmac_clk_reg, int interface) ++{ ++ unsigned int delay; ++ ++ if (gmac_clk_reg == NULL) ++ return; ++ ++ if (of_property_read_u32(pdev->dev.of_node, "rx-clk-delay", ++ &delay) == 0) { ++ /* RX clk delay */ ++ writel(delay, gmac_clk_reg + GMAC_CLK_CFG1); ++ pr_info("RX clk delay: 0x%X\n", delay); ++ } ++ ++ if (of_property_read_u32(pdev->dev.of_node, "tx-clk-delay", ++ &delay) == 0) { ++ /* TX clk delay */ ++ writel(delay, gmac_clk_reg + GMAC_CLK_CFG2); ++ pr_info("TX clk delay: 0x%X\n", delay); ++ } ++} ++ ++/* set gmac pll divider (u_pll_clk_div) to get 250MHz clock */ ++static void thead_dwmac_set_pll_250M(void __iomem *gmac_clk_reg, int interface, ++ unsigned int src_freq) ++{ ++ volatile unsigned int reg; ++ unsigned int div = 1; ++ ++ if (gmac_clk_reg == NULL) ++ return; ++ ++ if (interface == PHY_INTERFACE_MODE_MII) { ++ /* For MII, no internal PLL is used */ ++ return; ++ } else if (interface == PHY_INTERFACE_MODE_GMII ++ || interface == PHY_INTERFACE_MODE_RGMII ++ || interface == PHY_INTERFACE_MODE_RGMII_ID ++ || interface == PHY_INTERFACE_MODE_RGMII_RXID ++ || interface == PHY_INTERFACE_MODE_RGMII_TXID) { ++ ++ /* check clock */ ++ if ((src_freq == 0) || (src_freq % GMAC_CLK_PLLOUT_250M != 0)) { ++ pr_err("error! invalid gmac pll freq %d\n", src_freq); ++ return; ++ } ++ div = src_freq / GMAC_CLK_PLLOUT_250M; ++ ++ /* disable pll_clk_div */ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG3); ++ reg &= ~BIT(31); ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG3); ++ ++ /* modify divider */ ++ writel(div, gmac_clk_reg + GMAC_CLK_CFG3); ++ ++ /* enable pll_clk_div */ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG3); ++ reg |= BIT(31); ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG3); ++ } else { ++ pr_err("phy interface %d not supported\n", interface); ++ return; ++ } ++} ++ ++/* set gmac speed */ ++static void thead_dwmac_set_speed(void __iomem *gmac_clk_reg, int interface, ++ unsigned int speed) ++{ ++ volatile unsigned int reg; ++ ++ if (gmac_clk_reg == NULL) ++ return; ++ ++ if (interface == PHY_INTERFACE_MODE_MII) { ++ /* For MII, no internal PLL is used */ ++ return; ++ } else if (interface == PHY_INTERFACE_MODE_GMII ++ || interface == PHY_INTERFACE_MODE_RGMII ++ || interface == PHY_INTERFACE_MODE_RGMII_ID ++ || interface == PHY_INTERFACE_MODE_RGMII_RXID ++ || interface == PHY_INTERFACE_MODE_RGMII_TXID) { ++ ++ /* disable gtx_clk_div */ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG4); ++ reg &= ~BIT(31); ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG4); ++ ++ /* ++ * modify divider ++ */ ++ /* gtx_clk_div */ ++ if (speed == SPEED_1000) { ++ writel(GMAC_CLKDIV_125M, gmac_clk_reg + GMAC_CLK_CFG4); ++ } else if (speed == SPEED_100) { ++ writel(GMAC_CLKDIV_25M, gmac_clk_reg + GMAC_CLK_CFG4); ++ } else { ++ writel(GMAC_CLKDIV_25M / 10, gmac_clk_reg + GMAC_CLK_CFG4); ++ } ++ ++ /* enable gtx_clk_div */ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG4); ++ reg |= BIT(31); ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG4); ++ } else { ++ pr_err("phy interface %d not supported\n", interface); ++ return; ++ } ++} ++ ++/* enable gmac clock */ ++static void thead_dwmac_enable_clock(struct platform_device *pdev, ++ void __iomem *gmac_clk_reg, int interface) ++{ ++ struct device *dev = &pdev->dev; ++ volatile unsigned int reg; ++ ++ if (gmac_clk_reg == NULL) ++ return; ++ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG0); ++ ++ /* enable gmac_hclk */ ++ reg |= BIT(14); ++ ++ if (interface == PHY_INTERFACE_MODE_MII) { ++ reg |= BIT(8); /* enable gmac_rx_clk */ ++ reg |= BIT(3); /* enable gmac_tx_clk */ ++ } else if (interface == PHY_INTERFACE_MODE_GMII) { ++ reg |= BIT(8); /* enable gmac_rx_clk */ ++ reg |= BIT(3); /* enable gmac_tx_clk */ ++ reg |= BIT(6); /* enable gmac_tx_clk_out */ ++ } else if (interface == PHY_INTERFACE_MODE_RGMII ++ || interface == PHY_INTERFACE_MODE_RGMII_ID ++ || interface == PHY_INTERFACE_MODE_RGMII_RXID ++ || interface == PHY_INTERFACE_MODE_RGMII_TXID) { ++ reg |= BIT(8); /* enable gmac_rx_clk */ ++ reg |= BIT(3); /* enable gmac_tx_clk */ ++ reg |= BIT(6); /* enable gmac_tx_clk_out */ ++ reg |= BIT(9); /* enable gmac_rx_clk_n */ ++ reg |= BIT(4); /* enable gmac_tx_clk_n */ ++ } else { ++ dev_err(dev, "phy interface %d not supported\n", interface); ++ return; ++ } ++ ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG0); ++} ++ ++#if 0 ++/* disable gmac clock */ ++static void thead_dwmac_disable_clock(struct platform_device *pdev, ++ void __iomem *gmac_clk_reg, int interface) ++{ ++ struct device *dev = &pdev->dev; ++ volatile unsigned int reg; ++ ++ if (gmac_clk_reg == NULL) ++ return; ++ ++ reg = readl(gmac_clk_reg + GMAC_CLK_CFG0); ++ ++ /* disable gmac_hclk */ ++ reg &= ~BIT(14); ++ ++ if (interface == PHY_INTERFACE_MODE_MII) { ++ reg &= ~BIT(8); /* disable gmac_rx_clk */ ++ reg &= ~BIT(3); /* disable gmac_tx_clk */ ++ } else if (interface == PHY_INTERFACE_MODE_GMII) { ++ reg &= ~BIT(8); /* disable gmac_rx_clk */ ++ reg &= ~BIT(3); /* disable gmac_tx_clk */ ++ reg &= ~BIT(6); /* disable gmac_tx_clk_out */ ++ } else if (interface == PHY_INTERFACE_MODE_RGMII ++ || interface == PHY_INTERFACE_MODE_RGMII_ID ++ || interface == PHY_INTERFACE_MODE_RGMII_RXID ++ || interface == PHY_INTERFACE_MODE_RGMII_TXID) { ++ reg &= ~BIT(8); /* disable gmac_rx_clk */ ++ reg &= ~BIT(3); /* disable gmac_tx_clk */ ++ reg &= ~BIT(6); /* disable gmac_tx_clk_out */ ++ reg &= ~BIT(9); /* disable gmac_rx_clk_n */ ++ reg &= ~BIT(4); /* disable gmac_tx_clk_n */ ++ } else { ++ dev_err(dev, "phy interface %d not supported\n", interface); ++ return; ++ } ++ ++ writel(reg, gmac_clk_reg + GMAC_CLK_CFG0); ++} ++#endif ++ ++static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv) ++{ ++ struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *res; ++ void __iomem *ptr; ++ struct clk *clktmp; ++ int ret; ++ ++ thead_plat_dat->id = of_alias_get_id(np, "ethernet"); ++ if (thead_plat_dat->id < 0) { ++ thead_plat_dat->id = 0; ++ } ++ dev_info(dev, "id: %d\n", thead_plat_dat->id); ++ ++ if (of_get_phy_mode(dev->of_node, &(thead_plat_dat->interface))) { ++ dev_err(dev, "of_get_phy_mode error\n"); ++ return -1; ++ } ++ ++ dev_info(dev, "phy interface: %d\n", thead_plat_dat->interface); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_if_reg"); ++ if ((res != NULL) && (resource_type(res) == IORESOURCE_MEM)) { ++ ptr = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ptr) { ++ dev_err(dev, "phy interface register not exist, skipped it\n"); ++ } else { ++ thead_plat_dat->phy_if_reg = ptr; ++ } ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txclk_dir_reg"); ++ ptr = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ptr)) { ++ dev_err(dev, "txclk_dir register not exist, skipped it\n"); ++ } else { ++ thead_plat_dat->txclk_dir_reg = ptr; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_mgr_reg"); ++ ptr = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ptr)) { ++ dev_err(dev, "gmac_clk register not exist, skipped it\n"); ++ } else { ++ thead_plat_dat->gmac_clk_reg = ptr; ++ } ++ ++ /* get gmac pll clk */ ++ clktmp = devm_clk_get(dev, "gmac_pll_clk"); ++ if (IS_ERR(clktmp)) { ++ dev_err(dev, "gmac_pll_clk not exist, skipped it\n"); ++ } else { ++ thead_plat_dat->gmac_pll_clk = clktmp; ++ ++ ret = clk_prepare_enable(thead_plat_dat->gmac_pll_clk); ++ if (ret) { ++ dev_err(dev, "Failed to enable clk 'gmac_pll_clk'\n"); ++ return -1; ++ } ++ ++ thead_plat_dat->gmac_pll_clk_freq = ++ clk_get_rate(thead_plat_dat->gmac_pll_clk); ++ } ++ ++ thead_dwmac_set_phy_if(pdev, thead_plat_dat->phy_if_reg, ++ thead_plat_dat->interface, thead_plat_dat->id); ++ ++ thead_dwmac_set_txclk_dir(pdev, thead_plat_dat->txclk_dir_reg, ++ thead_plat_dat->interface); ++ ++ thead_dwmac_set_clk_source(pdev, thead_plat_dat->gmac_clk_reg, ++ thead_plat_dat->interface); ++ thead_dwmac_set_clock_delay(pdev, thead_plat_dat->gmac_clk_reg, ++ thead_plat_dat->interface); ++ ++ thead_dwmac_set_pll_250M(thead_plat_dat->gmac_clk_reg, ++ thead_plat_dat->interface, ++ thead_plat_dat->gmac_pll_clk_freq); ++ ++ /* default speed is 1Gbps */ ++ thead_dwmac_set_speed(thead_plat_dat->gmac_clk_reg, ++ thead_plat_dat->interface, SPEED_1000); ++ ++ thead_dwmac_enable_clock(pdev, thead_plat_dat->gmac_clk_reg, ++ thead_plat_dat->interface); ++ return 0; ++} ++ ++static void thead_dwmac_fix_speed(void *bsp_priv, unsigned int speed) ++{ ++ struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv; ++ ++ thead_dwmac_set_speed(thead_plat_dat->gmac_clk_reg, ++ thead_plat_dat->interface, speed); ++} ++ ++/** ++ * 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 ++ * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is ++ * invalid and will cause the filtering algorithm to use Multicast ++ * promiscuous mode. ++ */ ++static int dwmac1000_validate_mcast_bins(int mcast_bins) ++{ ++ int x = mcast_bins; ++ ++ switch (x) { ++ case HASH_TABLE_SIZE: ++ case 128: ++ case 256: ++ break; ++ default: ++ x = 0; ++ pr_info("Hash table entries set to unexpected value %d", ++ mcast_bins); ++ break; ++ } ++ return x; ++} ++ ++/** ++ * 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 ++ * selected, and defaults to 1 Unicast address if an unsupported ++ * configuration is selected. ++ */ ++static int dwmac1000_validate_ucast_entries(int ucast_entries) ++{ ++ int x = ucast_entries; ++ ++ switch (x) { ++ case 1 ... 32: ++ case 64: ++ case 128: ++ break; ++ default: ++ x = 1; ++ pr_info("Unicast table entries set to unexpected value %d\n", ++ ucast_entries); ++ break; ++ } ++ return x; ++} ++ ++static int thead_dwmac_probe(struct platform_device *pdev) ++{ ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ struct thead_dwmac_priv_data *thead_plat_dat; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = pdev->dev.of_node; ++ int ret; ++ ++ thead_plat_dat = devm_kzalloc(dev, sizeof(*thead_plat_dat), GFP_KERNEL); ++ if (thead_plat_dat == NULL) { ++ dev_err(&pdev->dev, "allocate memory failed\n"); ++ return -ENOMEM; ++ } ++ ++ 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) ++ goto err_remove_config_dt; ++ } ++ ++ /* populate bsp private data */ ++ plat_dat->bsp_priv = thead_plat_dat; ++ plat_dat->fix_mac_speed = thead_dwmac_fix_speed; ++ of_property_read_u32(np, "max-frame-size", &plat_dat->maxmtu); ++ of_property_read_u32(np, "snps,multicast-filter-bins", ++ &plat_dat->multicast_filter_bins); ++ of_property_read_u32(np, "snps,perfect-filter-entries", ++ &plat_dat->unicast_filter_entries); ++ plat_dat->unicast_filter_entries = dwmac1000_validate_ucast_entries( ++ plat_dat->unicast_filter_entries); ++ plat_dat->multicast_filter_bins = dwmac1000_validate_mcast_bins( ++ plat_dat->multicast_filter_bins); ++ plat_dat->has_gmac = 1; ++ plat_dat->pmt = 1; ++ ++ ret = thead_dwmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ goto err_exit; ++ ++ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++ if (ret) ++ goto err_exit; ++ ++ return 0; ++ ++err_exit: ++ if (plat_dat->exit) ++ plat_dat->exit(pdev, plat_dat->bsp_priv); ++err_remove_config_dt: ++ if (pdev->dev.of_node) ++ stmmac_remove_config_dt(pdev, plat_dat); ++ ++ return ret; ++} ++ ++static const struct of_device_id thead_dwmac_match[] = { ++ { .compatible = "thead,dwmac"}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, thead_dwmac_match); ++ ++static struct platform_driver thead_dwmac_driver = { ++ .probe = thead_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "thead_dwmac_eth", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = of_match_ptr(thead_dwmac_match), ++ }, ++}; ++module_platform_driver(thead_dwmac_driver); ++ ++MODULE_DESCRIPTION("T-HEAD dwmac driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.17.1 +