diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 22 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 91 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 33 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c | 285 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 7 |
9 files changed, 420 insertions, 38 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 53f14c5a9e02..e675ba12fde2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -219,6 +219,14 @@ config DWMAC_INTEL_PLAT This selects the Intel platform specific glue layer support for the stmmac device driver. This driver is used for the Intel Keem Bay SoC. + +config DWMAC_VISCONTI + tristate "Toshiba Visconti DWMAC support" + default ARCH_VISCONTI + depends on OF && COMMON_CLK && (ARCH_VISCONTI || COMPILE_TEST) + help + Support for ethernet controller on Visconti SoCs. + endif config DWMAC_INTEL diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 24e6145d4eae..366740ab9c5a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o +obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o stmmac-platform-objs:= stmmac_platform.o dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c index 82b1c7a5a7a9..6c19fcc76c6f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c @@ -74,8 +74,6 @@ MODULE_DEVICE_TABLE(of, intel_eth_plat_match); static int intel_eth_plat_probe(struct platform_device *pdev) { - struct net_device *ndev = platform_get_drvdata(pdev); - struct stmmac_priv *priv = netdev_priv(ndev); struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; const struct of_device_id *match; @@ -83,7 +81,6 @@ static int intel_eth_plat_probe(struct platform_device *pdev) unsigned long rate; int ret; - plat_dat = priv->plat; ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; @@ -129,7 +126,7 @@ static int intel_eth_plat_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to set tx_clk\n"); - return ret; + goto err_remove_config_dt; } } } @@ -143,7 +140,7 @@ static int intel_eth_plat_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to set clk_ptp_ref\n"); - return ret; + goto err_remove_config_dt; } } } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 9a6a519426a0..751dfdeec41c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -236,6 +236,7 @@ static int intel_mgbe_common_data(struct pci_dev *pdev, int ret; int i; + plat->pdev = pdev; plat->phy_addr = -1; plat->clk_csr = 5; plat->has_gmac = 0; @@ -375,6 +376,7 @@ static int ehl_pse0_common_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { plat->bus_id = 2; + plat->addr64 = 32; return ehl_common_data(pdev, plat); } @@ -406,6 +408,7 @@ static int ehl_pse1_common_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { plat->bus_id = 3; + plat->addr64 = 32; return ehl_common_data(pdev, plat); } @@ -457,6 +460,21 @@ static struct stmmac_pci_info tgl_sgmii1g_info = { .setup = tgl_sgmii_data, }; +static int adls_sgmii_data(struct pci_dev *pdev, + struct plat_stmmacenet_data *plat) +{ + plat->bus_id = 1; + plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + + /* SerDes power up and power down are done in BIOS for ADL */ + + return tgl_common_data(pdev, plat); +} + +static struct stmmac_pci_info adls_sgmii1g_info = { + .setup = adls_sgmii_data, +}; + static const struct stmmac_pci_func_data galileo_stmmac_func_data[] = { { .func = 6, @@ -724,6 +742,8 @@ static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend, #define PCI_DEVICE_ID_INTEL_TGLH_SGMII1G_0_ID 0x43ac #define PCI_DEVICE_ID_INTEL_TGLH_SGMII1G_1_ID 0x43a2 #define PCI_DEVICE_ID_INTEL_TGL_SGMII1G_ID 0xa0ac +#define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_0_ID 0x7aac +#define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_1_ID 0x7aad static const struct pci_device_id intel_eth_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_info) }, @@ -739,6 +759,8 @@ static const struct pci_device_id intel_eth_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_info) }, { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_0_ID, &tgl_sgmii1g_info) }, { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_1_ID, &tgl_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_0_ID, &adls_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_1_ID, &adls_sgmii1g_info) }, {} }; MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index f184b00f5116..848e5c37746b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -68,10 +68,21 @@ */ #define PRG_ETH0_ADJ_SKEW GENMASK(24, 20) +#define PRG_ETH1 0x4 + +/* Defined for adding a delay to the input RX_CLK for better timing. + * Each step is 200ps. These bits are used with external RGMII PHYs + * because RGMII RX only has the small window. cfg_rxclk_dly can + * adjust the window between RX_CLK and RX_DATA and improve the stability + * of "rx data valid". + */ +#define PRG_ETH1_CFG_RXCLK_DLY GENMASK(19, 16) + struct meson8b_dwmac; struct meson8b_dwmac_data { int (*set_phy_mode)(struct meson8b_dwmac *dwmac); + bool has_prg_eth1_rgmii_rx_delay; }; struct meson8b_dwmac { @@ -82,7 +93,7 @@ struct meson8b_dwmac { phy_interface_t phy_mode; struct clk *rgmii_tx_clk; u32 tx_delay_ns; - u32 rx_delay_ns; + u32 rx_delay_ps; struct clk *timing_adj_clk; }; @@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, return 0; } -static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) +static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac) { - u32 tx_dly_config, rx_dly_config, delay_config; + u32 tx_dly_config, rx_adj_config, cfg_rxclk_dly, delay_config; int ret; + rx_adj_config = 0; + cfg_rxclk_dly = 0; tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK, dwmac->tx_delay_ns >> 1); - if (dwmac->rx_delay_ns == 2) - rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP; - else - rx_dly_config = 0; + if (dwmac->data->has_prg_eth1_rgmii_rx_delay) + cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY, + dwmac->rx_delay_ps / 200); + else if (dwmac->rx_delay_ps == 2000) + rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP; switch (dwmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: - delay_config = tx_dly_config | rx_dly_config; + delay_config = tx_dly_config | rx_adj_config; break; case PHY_INTERFACE_MODE_RGMII_RXID: delay_config = tx_dly_config; + cfg_rxclk_dly = 0; break; case PHY_INTERFACE_MODE_RGMII_TXID: - delay_config = rx_dly_config; + delay_config = rx_adj_config; break; case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RMII: delay_config = 0; + cfg_rxclk_dly = 0; break; default: dev_err(dwmac->dev, "unsupported phy-mode %s\n", @@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) return -EINVAL; } - if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) { + if (delay_config & PRG_ETH0_ADJ_ENABLE) { if (!dwmac->timing_adj_clk) { dev_err(dwmac->dev, "The timing-adjustment clock is mandatory for the RX delay re-timing\n"); @@ -323,6 +339,16 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW, delay_config); + meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY, + cfg_rxclk_dly); + + return 0; +} + +static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) +{ + int ret; + if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) { /* only relevant for RMII mode -> disable in RGMII mode */ meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, @@ -406,16 +432,30 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) &dwmac->tx_delay_ns)) dwmac->tx_delay_ns = 2; - /* use 0ns as fallback since this is what most boards actually use */ - if (of_property_read_u32(pdev->dev.of_node, "amlogic,rx-delay-ns", - &dwmac->rx_delay_ns)) - dwmac->rx_delay_ns = 0; + /* RX delay defaults to 0ps since this is what many boards use */ + if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps", + &dwmac->rx_delay_ps)) { + if (!of_property_read_u32(pdev->dev.of_node, + "amlogic,rx-delay-ns", + &dwmac->rx_delay_ps)) + /* convert ns to ps */ + dwmac->rx_delay_ps *= 1000; + } - if (dwmac->rx_delay_ns != 0 && dwmac->rx_delay_ns != 2) { - dev_err(&pdev->dev, - "The only allowed RX delays values are: 0ns, 2ns"); - ret = -EINVAL; - goto err_remove_config_dt; + if (dwmac->data->has_prg_eth1_rgmii_rx_delay) { + if (dwmac->rx_delay_ps > 3000 || dwmac->rx_delay_ps % 200) { + dev_err(dwmac->dev, + "The RGMII RX delay range is 0..3000ps in 200ps steps"); + ret = -EINVAL; + goto err_remove_config_dt; + } + } else { + if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) { + dev_err(dwmac->dev, + "The only allowed RGMII RX delays values are: 0ps, 2000ps"); + ret = -EINVAL; + goto err_remove_config_dt; + } } dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev, @@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) goto err_remove_config_dt; } + ret = meson8b_init_rgmii_delays(dwmac); + if (ret) + goto err_remove_config_dt; + ret = meson8b_init_rgmii_tx_clk(dwmac); if (ret) goto err_remove_config_dt; @@ -453,10 +497,17 @@ err_remove_config_dt: static const struct meson8b_dwmac_data meson8b_dwmac_data = { .set_phy_mode = meson8b_set_phy_mode, + .has_prg_eth1_rgmii_rx_delay = false, }; static const struct meson8b_dwmac_data meson_axg_dwmac_data = { .set_phy_mode = meson_axg_set_phy_mode, + .has_prg_eth1_rgmii_rx_delay = false, +}; + +static const struct meson8b_dwmac_data meson_g12a_dwmac_data = { + .set_phy_mode = meson_axg_set_phy_mode, + .has_prg_eth1_rgmii_rx_delay = true, }; static const struct of_device_id meson8b_dwmac_match[] = { @@ -478,7 +529,7 @@ static const struct of_device_id meson8b_dwmac_match[] = { }, { .compatible = "amlogic,meson-g12a-dwmac", - .data = &meson_axg_dwmac_data, + .data = &meson_g12a_dwmac_data, }, { } }; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index a5e0eff4a387..6b75cf2603ff 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -805,12 +805,12 @@ static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv) /* Make sure the EPHY is properly reseted, as U-Boot may leave * it at deasserted state, and thus it may fail to reset EMAC. + * + * This assumes the driver has exclusive access to the EPHY reset. */ - reset_control_assert(gmac->rst_ephy); - - ret = reset_control_deassert(gmac->rst_ephy); + ret = reset_control_reset(gmac->rst_ephy); if (ret) { - dev_err(priv->device, "Cannot deassert internal phy\n"); + dev_err(priv->device, "Cannot reset internal PHY\n"); clk_disable_unprepare(gmac->ephy_clk); return ret; } @@ -820,15 +820,14 @@ static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv) return 0; } -static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac) +static void sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac) { if (!gmac->internal_phy_powered) - return 0; + return; clk_disable_unprepare(gmac->ephy_clk); reset_control_assert(gmac->rst_ephy); gmac->internal_phy_powered = false; - return 0; } /* MDIO multiplexing switch function @@ -1019,10 +1018,8 @@ static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) { struct sunxi_priv_data *gmac = priv; - if (gmac->variant->soc_has_internal_phy) { - if (gmac->internal_phy_powered) - sun8i_dwmac_unpower_internal_phy(gmac); - } + if (gmac->variant->soc_has_internal_phy) + sun8i_dwmac_unpower_internal_phy(gmac); clk_disable_unprepare(gmac->tx_clk); @@ -1232,6 +1229,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) ndev = dev_get_drvdata(&pdev->dev); priv = netdev_priv(ndev); + /* The mux must be registered after parent MDIO * so after stmmac_dvr_probe() */ @@ -1250,7 +1248,8 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) goto dwmac_remove; } - return ret; + return 0; + dwmac_mux: reset_control_put(gmac->rst_ephy); clk_put(gmac->ephy_clk); @@ -1285,6 +1284,15 @@ static int sun8i_dwmac_remove(struct platform_device *pdev) return 0; } +static void sun8i_dwmac_shutdown(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct sunxi_priv_data *gmac = priv->plat->bsp_priv; + + sun8i_dwmac_exit(pdev, gmac); +} + static const struct of_device_id sun8i_dwmac_match[] = { { .compatible = "allwinner,sun8i-h3-emac", .data = &emac_variant_h3 }, @@ -1305,6 +1313,7 @@ MODULE_DEVICE_TABLE(of, sun8i_dwmac_match); static struct platform_driver sun8i_dwmac_driver = { .probe = sun8i_dwmac_probe, .remove = sun8i_dwmac_remove, + .shutdown = sun8i_dwmac_shutdown, .driver = { .name = "dwmac-sun8i", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c new file mode 100644 index 000000000000..b7a0c57dfbfb --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Toshiba Visconti Ethernet Support + * + * (C) Copyright 2020 TOSHIBA CORPORATION + * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation + */ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_net.h> +#include <linux/stmmac.h> + +#include "stmmac_platform.h" +#include "dwmac4.h" + +#define REG_ETHER_CONTROL 0x52D4 +#define ETHER_ETH_CONTROL_RESET BIT(17) + +#define REG_ETHER_CLOCK_SEL 0x52D0 +#define ETHER_CLK_SEL_TX_CLK_EN BIT(0) +#define ETHER_CLK_SEL_RX_CLK_EN BIT(1) +#define ETHER_CLK_SEL_RMII_CLK_EN BIT(2) +#define ETHER_CLK_SEL_RMII_CLK_RST BIT(3) +#define ETHER_CLK_SEL_DIV_SEL_2 BIT(4) +#define ETHER_CLK_SEL_DIV_SEL_20 BIT(0) +#define ETHER_CLK_SEL_FREQ_SEL_125M (BIT(9) | BIT(8)) +#define ETHER_CLK_SEL_FREQ_SEL_50M BIT(9) +#define ETHER_CLK_SEL_FREQ_SEL_25M BIT(8) +#define ETHER_CLK_SEL_FREQ_SEL_2P5M BIT(0) +#define ETHER_CLK_SEL_TX_CLK_EXT_SEL_IN BIT(0) +#define ETHER_CLK_SEL_TX_CLK_EXT_SEL_TXC BIT(10) +#define ETHER_CLK_SEL_TX_CLK_EXT_SEL_DIV BIT(11) +#define ETHER_CLK_SEL_RX_CLK_EXT_SEL_IN BIT(0) +#define ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC BIT(12) +#define ETHER_CLK_SEL_RX_CLK_EXT_SEL_DIV BIT(13) +#define ETHER_CLK_SEL_TX_CLK_O_TX_I BIT(0) +#define ETHER_CLK_SEL_TX_CLK_O_RMII_I BIT(14) +#define ETHER_CLK_SEL_TX_O_E_N_IN BIT(15) +#define ETHER_CLK_SEL_RMII_CLK_SEL_IN BIT(0) +#define ETHER_CLK_SEL_RMII_CLK_SEL_RX_C BIT(16) + +#define ETHER_CLK_SEL_RX_TX_CLK_EN (ETHER_CLK_SEL_RX_CLK_EN | ETHER_CLK_SEL_TX_CLK_EN) + +#define ETHER_CONFIG_INTF_MII 0 +#define ETHER_CONFIG_INTF_RGMII BIT(0) +#define ETHER_CONFIG_INTF_RMII BIT(2) + +struct visconti_eth { + void __iomem *reg; + u32 phy_intf_sel; + struct clk *phy_ref_clk; + spinlock_t lock; /* lock to protect register update */ +}; + +static void visconti_eth_fix_mac_speed(void *priv, unsigned int speed) +{ + struct visconti_eth *dwmac = priv; + unsigned int val, clk_sel_val; + unsigned long flags; + + spin_lock_irqsave(&dwmac->lock, flags); + + /* adjust link */ + val = readl(dwmac->reg + MAC_CTRL_REG); + val &= ~(GMAC_CONFIG_PS | GMAC_CONFIG_FES); + + switch (speed) { + case SPEED_1000: + if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RGMII) + clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_125M; + break; + case SPEED_100: + if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RGMII) + clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_25M; + if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RMII) + clk_sel_val = ETHER_CLK_SEL_DIV_SEL_2; + val |= GMAC_CONFIG_PS | GMAC_CONFIG_FES; + break; + case SPEED_10: + if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RGMII) + clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_2P5M; + if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RMII) + clk_sel_val = ETHER_CLK_SEL_DIV_SEL_20; + val |= GMAC_CONFIG_PS; + break; + default: + /* No bit control */ + break; + } + + writel(val, dwmac->reg + MAC_CTRL_REG); + + /* Stop internal clock */ + val = readl(dwmac->reg + REG_ETHER_CLOCK_SEL); + val &= ~(ETHER_CLK_SEL_RMII_CLK_EN | ETHER_CLK_SEL_RX_TX_CLK_EN); + val |= ETHER_CLK_SEL_TX_O_E_N_IN; + writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); + + switch (dwmac->phy_intf_sel) { + case ETHER_CONFIG_INTF_RGMII: + val = clk_sel_val | ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC; + break; + case ETHER_CONFIG_INTF_RMII: + val = clk_sel_val | ETHER_CLK_SEL_RX_CLK_EXT_SEL_DIV | + ETHER_CLK_SEL_TX_CLK_EXT_SEL_TXC | ETHER_CLK_SEL_TX_O_E_N_IN | + ETHER_CLK_SEL_RMII_CLK_SEL_RX_C; + break; + case ETHER_CONFIG_INTF_MII: + default: + val = clk_sel_val | ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC | + ETHER_CLK_SEL_TX_CLK_EXT_SEL_DIV | ETHER_CLK_SEL_TX_O_E_N_IN | + ETHER_CLK_SEL_RMII_CLK_EN; + break; + } + + /* Start clock */ + writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); + val |= ETHER_CLK_SEL_RX_TX_CLK_EN; + writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); + + val &= ~ETHER_CLK_SEL_TX_O_E_N_IN; + writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); + + spin_unlock_irqrestore(&dwmac->lock, flags); +} + +static int visconti_eth_init_hw(struct platform_device *pdev, struct plat_stmmacenet_data *plat_dat) +{ + struct visconti_eth *dwmac = plat_dat->bsp_priv; + unsigned int reg_val, clk_sel_val; + + switch (plat_dat->phy_interface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + dwmac->phy_intf_sel = ETHER_CONFIG_INTF_RGMII; + break; + case PHY_INTERFACE_MODE_MII: + dwmac->phy_intf_sel = ETHER_CONFIG_INTF_MII; + break; + case PHY_INTERFACE_MODE_RMII: + dwmac->phy_intf_sel = ETHER_CONFIG_INTF_RMII; + break; + default: + dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", plat_dat->phy_interface); + return -EOPNOTSUPP; + } + + reg_val = dwmac->phy_intf_sel; + writel(reg_val, dwmac->reg + REG_ETHER_CONTROL); + + /* Enable TX/RX clock */ + clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_125M; + writel(clk_sel_val, dwmac->reg + REG_ETHER_CLOCK_SEL); + + writel((clk_sel_val | ETHER_CLK_SEL_RMII_CLK_EN | ETHER_CLK_SEL_RX_TX_CLK_EN), + dwmac->reg + REG_ETHER_CLOCK_SEL); + + /* release internal-reset */ + reg_val |= ETHER_ETH_CONTROL_RESET; + writel(reg_val, dwmac->reg + REG_ETHER_CONTROL); + + return 0; +} + +static int visconti_eth_clock_probe(struct platform_device *pdev, + struct plat_stmmacenet_data *plat_dat) +{ + struct visconti_eth *dwmac = plat_dat->bsp_priv; + int err; + + dwmac->phy_ref_clk = devm_clk_get(&pdev->dev, "phy_ref_clk"); + if (IS_ERR(dwmac->phy_ref_clk)) { + dev_err(&pdev->dev, "phy_ref_clk clock not found.\n"); + return PTR_ERR(dwmac->phy_ref_clk); + } + + err = clk_prepare_enable(dwmac->phy_ref_clk); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable phy_ref clock: %d\n", err); + return err; + } + + return 0; +} + +static int visconti_eth_clock_remove(struct platform_device *pdev) +{ + struct visconti_eth *dwmac = get_stmmac_bsp_priv(&pdev->dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + + clk_disable_unprepare(dwmac->phy_ref_clk); + clk_disable_unprepare(priv->plat->stmmac_clk); + + return 0; +} + +static int visconti_eth_dwmac_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct visconti_eth *dwmac; + 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) { + ret = -ENOMEM; + goto remove_config; + } + + dwmac->reg = stmmac_res.addr; + plat_dat->bsp_priv = dwmac; + plat_dat->fix_mac_speed = visconti_eth_fix_mac_speed; + + ret = visconti_eth_clock_probe(pdev, plat_dat); + if (ret) + goto remove_config; + + visconti_eth_init_hw(pdev, plat_dat); + + plat_dat->dma_cfg->aal = 1; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto remove; + + return ret; + +remove: + visconti_eth_clock_remove(pdev); +remove_config: + stmmac_remove_config_dt(pdev, plat_dat); + + return ret; +} + +static int visconti_eth_dwmac_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + int err; + + err = stmmac_pltfr_remove(pdev); + if (err < 0) + dev_err(&pdev->dev, "failed to remove platform: %d\n", err); + + err = visconti_eth_clock_remove(pdev); + if (err < 0) + dev_err(&pdev->dev, "failed to remove clock: %d\n", err); + + stmmac_remove_config_dt(pdev, priv->plat); + + return err; +} + +static const struct of_device_id visconti_eth_dwmac_match[] = { + { .compatible = "toshiba,visconti-dwmac" }, + { } +}; +MODULE_DEVICE_TABLE(of, visconti_eth_dwmac_match); + +static struct platform_driver visconti_eth_dwmac_driver = { + .probe = visconti_eth_dwmac_probe, + .remove = visconti_eth_dwmac_remove, + .driver = { + .name = "visconti-eth-dwmac", + .of_match_table = visconti_eth_dwmac_match, + }, +}; +module_platform_driver(visconti_eth_dwmac_driver); + +MODULE_AUTHOR("Toshiba"); +MODULE_DESCRIPTION("Toshiba Visconti Ethernet DWMAC glue driver"); +MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 9e54f953634b..c5642985ef95 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -268,6 +268,10 @@ static void stmmac_ethtool_getdrvinfo(struct net_device *dev, strlcpy(info->driver, MAC100_ETHTOOL_NAME, sizeof(info->driver)); + if (priv->plat->pdev) { + strlcpy(info->bus_info, pci_name(priv->plat->pdev), + sizeof(info->bus_info)); + } strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 8ed3b2c834a0..56985542e202 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -324,7 +324,12 @@ static int tc_setup_cbs(struct stmmac_priv *priv, priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB; } else if (!qopt->enable) { - return stmmac_dma_qmode(priv, priv->ioaddr, queue, MTL_QUEUE_DCB); + ret = stmmac_dma_qmode(priv, priv->ioaddr, queue, + MTL_QUEUE_DCB); + if (ret) + return ret; + + priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; } /* Port Transmit Rate and Speed Divider */ |