diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro')
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 437 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 13 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 31 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 113 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 | 
7 files changed, 588 insertions, 13 deletions
| diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index ac4d5629d905..73c2715a27f3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o	\  obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o  stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o	\ -		       dwmac-sti.o dwmac-socfpga.o +		       dwmac-sti.o dwmac-socfpga.o dwmac-rk.o  obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o  stmmac-pci-objs:= stmmac_pci.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c new file mode 100644 index 000000000000..6249a4ec08f0 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -0,0 +1,437 @@ +/** + * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer + * + * Copyright (C) 2014 Chen-Zhi (Roger Chen) + * + * Chen-Zhi (Roger Chen)  <[email protected]> + * + * 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/of_gpio.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +struct rk_priv_data { +	struct platform_device *pdev; +	int phy_iface; +	struct regulator *regulator; + +	bool clk_enabled; +	bool clock_input; + +	struct clk *clk_mac; +	struct clk *clk_mac_pll; +	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 +#define RK3288_GRF_GPIO3D_E	0x01ec +#define RK3288_GRF_GPIO4A_E	0x01f0 +#define RK3288_GRF_GPIO4B_E	0x01f4 + +/*RK3288_GRF_SOC_CON1*/ +#define GMAC_PHY_INTF_SEL_RGMII	(GRF_BIT(6) | GRF_CLR_BIT(7) | GRF_CLR_BIT(8)) +#define GMAC_PHY_INTF_SEL_RMII	(GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | GRF_BIT(8)) +#define GMAC_FLOW_CTRL		GRF_BIT(9) +#define GMAC_FLOW_CTRL_CLR	GRF_CLR_BIT(9) +#define GMAC_SPEED_10M		GRF_CLR_BIT(10) +#define GMAC_SPEED_100M		GRF_BIT(10) +#define GMAC_RMII_CLK_25M	GRF_BIT(11) +#define GMAC_RMII_CLK_2_5M	GRF_CLR_BIT(11) +#define GMAC_CLK_125M		(GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) +#define GMAC_CLK_25M		(GRF_BIT(12) | GRF_BIT(13)) +#define GMAC_CLK_2_5M		(GRF_CLR_BIT(12) | GRF_BIT(13)) +#define GMAC_RMII_MODE		GRF_BIT(14) +#define GMAC_RMII_MODE_CLR	GRF_CLR_BIT(14) + +/*RK3288_GRF_SOC_CON3*/ +#define GMAC_TXCLK_DLY_ENABLE	GRF_BIT(14) +#define GMAC_TXCLK_DLY_DISABLE	GRF_CLR_BIT(14) +#define GMAC_RXCLK_DLY_ENABLE	GRF_BIT(15) +#define GMAC_RXCLK_DLY_DISABLE	GRF_CLR_BIT(15) +#define GMAC_CLK_RX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 7) +#define GMAC_CLK_TX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 0) + +static void 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, RK3288_GRF_SOC_CON1, +		     GMAC_PHY_INTF_SEL_RGMII | GMAC_RMII_MODE_CLR); +	regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, +		     GMAC_RXCLK_DLY_ENABLE | GMAC_TXCLK_DLY_ENABLE | +		     GMAC_CLK_RX_DL_CFG(rx_delay) | +		     GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void 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, RK3288_GRF_SOC_CON1, +		     GMAC_PHY_INTF_SEL_RMII | GMAC_RMII_MODE); +} + +static void 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, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M); +	else if (speed == 100) +		regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M); +	else if (speed == 1000) +		regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M); +	else +		dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); +} + +static void 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, RK3288_GRF_SOC_CON1, +			     GMAC_RMII_CLK_2_5M | GMAC_SPEED_10M); +	} else if (speed == 100) { +		regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, +			     GMAC_RMII_CLK_25M | GMAC_SPEED_100M); +	} else { +		dev_err(dev, "unknown speed value for RMII! speed=%d", 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, "%s: cannot get clock %s\n", +			__func__, "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, "%s: cannot get clock %s\n", +			__func__, "mac_clk_tx"); + +	bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac"); +	if (IS_ERR(bsp_priv->aclk_mac)) +		dev_err(dev, "%s: cannot get clock %s\n", +			__func__, "aclk_mac"); + +	bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac"); +	if (IS_ERR(bsp_priv->pclk_mac)) +		dev_err(dev, "%s: cannot get clock %s\n", +			__func__, "pclk_mac"); + +	bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth"); +	if (IS_ERR(bsp_priv->clk_mac)) +		dev_err(dev, "%s: cannot get clock %s\n", +			__func__, "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, "%s: cannot get clock %s\n", +				__func__, "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, "%s: cannot get clock %s\n", +					__func__, "clk_mac_refout"); +		} +	} + +	if (bsp_priv->clock_input) { +		dev_info(dev, "%s: clock input from PHY\n", __func__); +	} else { +		if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) +			clk_set_rate(bsp_priv->clk_mac_pll, 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, "%s: no regulator found\n", __func__); +		return -1; +	} + +	if (enable) { +		ret = regulator_enable(ldo); +		if (ret) +			dev_err(dev, "%s: fail to enable phy-supply\n", +				__func__); +	} else { +		ret = regulator_disable(ldo); +		if (ret) +			dev_err(dev, "%s: fail to disable phy-supply\n", +				__func__); +	} + +	return 0; +} + +static void *rk_gmac_setup(struct platform_device *pdev) +{ +	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->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, "%s: Can not read property: clock_in_out.\n", +			__func__); +		bsp_priv->clock_input = true; +	} else { +		dev_info(dev, "%s: clock input or output? (%s).\n", +			 __func__, 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, "%s: Can not read property: tx_delay.", __func__); +		dev_err(dev, "%s: set tx_delay to 0x%x\n", +			__func__, bsp_priv->tx_delay); +	} else { +		dev_info(dev, "%s: TX delay(0x%x).\n", __func__, 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, "%s: Can not read property: rx_delay.", __func__); +		dev_err(dev, "%s: set rx_delay to 0x%x\n", +			__func__, bsp_priv->rx_delay); +	} else { +		dev_info(dev, "%s: RX delay(0x%x).\n", __func__, 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, "%s: init for RGMII\n", __func__); +		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, "%s: init for RMII\n", __func__); +		set_to_rmii(bsp_priv); +	} else { +		dev_err(dev, "%s: NO interface defined!\n", __func__); +	} + +	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) +		set_rgmii_speed(bsp_priv, speed); +	else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) +		set_rmii_speed(bsp_priv, speed); +	else +		dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); +} + +const struct stmmac_of_data rk3288_gmac_data = { +	.has_gmac = 1, +	.fix_mac_speed = rk_fix_speed, +	.setup = rk_gmac_setup, +	.init = rk_gmac_init, +	.exit = rk_gmac_exit, +}; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 056b358b4a72..bb6e2dc61bec 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -122,7 +122,7 @@ 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; @@ -285,11 +285,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,  	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"); @@ -300,6 +295,12 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,  	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; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8c6b7c1651e5..55e89b3838f1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1097,6 +1097,7 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)  	priv->dirty_tx = 0;  	priv->cur_tx = 0; +	netdev_reset_queue(priv->dev);  	stmmac_clear_descriptors(priv); @@ -1287,7 +1288,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)  		 *    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->xstats.threshold = SF_DMA_MODE;  	} else  		priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);  } @@ -1300,6 +1301,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)  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); @@ -1356,6 +1358,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)  		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;  		} @@ -1364,6 +1368,9 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)  		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); @@ -1418,6 +1425,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv)  						     (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++; @@ -1444,9 +1452,14 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)  	}  	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); +			else +				priv->hw->dma->dma_mode(priv->ioaddr, tc, +					SF_DMA_MODE);  			priv->xstats.threshold = tc;  		}  	} else if (unlikely(status == tx_hard_error)) @@ -2050,6 +2063,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)  	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); @@ -2742,7 +2756,11 @@ static int stmmac_hw_init(struct stmmac_priv *priv)  		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; @@ -2778,6 +2796,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv)   * @addr: iobase memory address   * Description: this is the main probe function used to   * call the alloc_etherdev, allocate the priv structure. + * Return: + * on success the new private structure is returned, otherwise the error + * pointer.   */  struct stmmac_priv *stmmac_dvr_probe(struct device *device,  				     struct plat_stmmacenet_data *plat_dat, @@ -2789,7 +2810,7 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,  	ndev = alloc_etherdev(sizeof(struct stmmac_priv));  	if (!ndev) -		return NULL; +		return ERR_PTR(-ENOMEM);  	SET_NETDEV_DEV(ndev, device); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 054520d67de4..3bca908716e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -24,8 +24,50 @@  *******************************************************************************/  #include <linux/pci.h> +#include <linux/dmi.h> +  #include "stmmac.h" +/* + * 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; @@ -48,6 +90,62 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat)  	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->multicast_filter_bins = HASH_TABLE_SIZE; + +	/* Set default value for unicast filter entries */ +	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   * @@ -63,6 +161,7 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat)  static int stmmac_pci_probe(struct pci_dev *pdev,  			    const struct pci_device_id *id)  { +	struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data;  	struct plat_stmmacenet_data *plat;  	struct stmmac_priv *priv;  	int i; @@ -103,7 +202,17 @@ static int stmmac_pci_probe(struct pci_dev *pdev,  	pci_set_master(pdev); -	stmmac_default_data(plat); +	if (info) { +		info->pdev = pdev; +		if (info->setup) { +			ret = info->setup(plat, info); +			if (ret) +				return ret; +		} +	} else +		stmmac_default_data(plat); + +	pci_enable_msi(pdev);  	priv = stmmac_dvr_probe(&pdev->dev, plat, pcim_iomap_table(pdev)[i]);  	if (IS_ERR(priv)) { @@ -155,11 +264,13 @@ static int stmmac_pci_resume(struct device *dev)  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},  	{}  }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 3039de2465ba..fb846ebba1d9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -33,6 +33,7 @@  static const struct of_device_id stmmac_dt_ids[] = {  	/* SoC specific glue layers should come before generic bindings */ +	{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_gmac_data},  	{ .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data},  	{ .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},  	{ .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, @@ -234,6 +235,9 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,  			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) { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h index 25dd1f7ace02..093eb99e5ffd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h @@ -24,5 +24,6 @@ extern const struct stmmac_of_data sun7i_gmac_data;  extern const struct stmmac_of_data stih4xx_dwmac_data;  extern const struct stmmac_of_data stid127_dwmac_data;  extern const struct stmmac_of_data socfpga_gmac_data; +extern const struct stmmac_of_data rk3288_gmac_data;  #endif /* __STMMAC_PLATFORM_H__ */ |