|
@@ -18,6 +18,7 @@
|
|
|
#include <linux/mfd/syscon.h>
|
|
|
#include <linux/regmap.h>
|
|
|
#include <linux/clk.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <linux/if_vlan.h>
|
|
|
#include <linux/reset.h>
|
|
|
#include <linux/tcp.h>
|
|
@@ -144,6 +145,9 @@ static void mtk_phy_link_adjust(struct net_device *dev)
|
|
|
MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
|
|
|
MAC_MCR_BACKPR_EN;
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
|
|
|
+ return;
|
|
|
+
|
|
|
switch (mac->phy_dev->speed) {
|
|
|
case SPEED_1000:
|
|
|
mcr |= MAC_MCR_SPEED_1000;
|
|
@@ -230,7 +234,7 @@ static int mtk_phy_connect(struct mtk_mac *mac)
|
|
|
{
|
|
|
struct mtk_eth *eth = mac->hw;
|
|
|
struct device_node *np;
|
|
|
- u32 val, ge_mode;
|
|
|
+ u32 val;
|
|
|
|
|
|
np = of_parse_phandle(mac->of_node, "phy-handle", 0);
|
|
|
if (!np && of_phy_is_fixed_link(mac->of_node))
|
|
@@ -244,18 +248,18 @@ static int mtk_phy_connect(struct mtk_mac *mac)
|
|
|
case PHY_INTERFACE_MODE_RGMII_RXID:
|
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
|
case PHY_INTERFACE_MODE_RGMII:
|
|
|
- ge_mode = 0;
|
|
|
+ mac->ge_mode = 0;
|
|
|
break;
|
|
|
case PHY_INTERFACE_MODE_MII:
|
|
|
- ge_mode = 1;
|
|
|
+ mac->ge_mode = 1;
|
|
|
break;
|
|
|
case PHY_INTERFACE_MODE_REVMII:
|
|
|
- ge_mode = 2;
|
|
|
+ mac->ge_mode = 2;
|
|
|
break;
|
|
|
case PHY_INTERFACE_MODE_RMII:
|
|
|
if (!mac->id)
|
|
|
goto err_phy;
|
|
|
- ge_mode = 3;
|
|
|
+ mac->ge_mode = 3;
|
|
|
break;
|
|
|
default:
|
|
|
goto err_phy;
|
|
@@ -264,7 +268,7 @@ static int mtk_phy_connect(struct mtk_mac *mac)
|
|
|
/* put the gmac into the right mode */
|
|
|
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
|
|
|
val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id);
|
|
|
- val |= SYSCFG0_GE_MODE(ge_mode, mac->id);
|
|
|
+ val |= SYSCFG0_GE_MODE(mac->ge_mode, mac->id);
|
|
|
regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
|
|
|
|
|
|
mtk_phy_connect_node(eth, mac, np);
|
|
@@ -369,6 +373,9 @@ static int mtk_set_mac_address(struct net_device *dev, void *p)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
spin_lock_bh(&mac->hw->page_lock);
|
|
|
mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1],
|
|
|
MTK_GDMA_MAC_ADRH(mac->id));
|
|
@@ -769,6 +776,9 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
*/
|
|
|
spin_lock(ð->page_lock);
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, ð->state)))
|
|
|
+ goto drop;
|
|
|
+
|
|
|
tx_num = mtk_cal_txd_req(skb);
|
|
|
if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
|
|
|
mtk_stop_queue(eth);
|
|
@@ -841,6 +851,9 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
|
|
|
|
|
|
netdev = eth->netdev[mac];
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, ð->state)))
|
|
|
+ goto release_desc;
|
|
|
+
|
|
|
/* alloc new buffer */
|
|
|
new_data = napi_alloc_frag(ring->frag_size);
|
|
|
if (unlikely(!new_data)) {
|
|
@@ -1413,15 +1426,44 @@ static int mtk_stop(struct net_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int __init mtk_hw_init(struct mtk_eth *eth)
|
|
|
+static void ethsys_reset(struct mtk_eth *eth, u32 reset_bits)
|
|
|
{
|
|
|
- int err, i;
|
|
|
+ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
|
|
|
+ reset_bits,
|
|
|
+ reset_bits);
|
|
|
+
|
|
|
+ usleep_range(1000, 1100);
|
|
|
+ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
|
|
|
+ reset_bits,
|
|
|
+ ~reset_bits);
|
|
|
+ mdelay(10);
|
|
|
+}
|
|
|
+
|
|
|
+static int mtk_hw_init(struct mtk_eth *eth)
|
|
|
+{
|
|
|
+ int i, val;
|
|
|
+
|
|
|
+ if (test_and_set_bit(MTK_HW_INIT, ð->state))
|
|
|
+ return 0;
|
|
|
|
|
|
- /* reset the frame engine */
|
|
|
- reset_control_assert(eth->rstc);
|
|
|
- usleep_range(10, 20);
|
|
|
- reset_control_deassert(eth->rstc);
|
|
|
- usleep_range(10, 20);
|
|
|
+ pm_runtime_enable(eth->dev);
|
|
|
+ pm_runtime_get_sync(eth->dev);
|
|
|
+
|
|
|
+ clk_prepare_enable(eth->clks[MTK_CLK_ETHIF]);
|
|
|
+ clk_prepare_enable(eth->clks[MTK_CLK_ESW]);
|
|
|
+ clk_prepare_enable(eth->clks[MTK_CLK_GP1]);
|
|
|
+ clk_prepare_enable(eth->clks[MTK_CLK_GP2]);
|
|
|
+ ethsys_reset(eth, RSTCTRL_FE);
|
|
|
+ ethsys_reset(eth, RSTCTRL_PPE);
|
|
|
+
|
|
|
+ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
|
|
|
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
|
|
|
+ if (!eth->mac[i])
|
|
|
+ continue;
|
|
|
+ val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, eth->mac[i]->id);
|
|
|
+ val |= SYSCFG0_GE_MODE(eth->mac[i]->ge_mode, eth->mac[i]->id);
|
|
|
+ }
|
|
|
+ regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
|
|
|
|
|
|
/* Set GE2 driving and slew rate */
|
|
|
regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
|
|
@@ -1441,19 +1483,6 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
|
|
|
/* Enable RX VLan Offloading */
|
|
|
mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
|
|
|
|
|
|
- err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
|
|
|
- dev_name(eth->dev), eth);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
|
|
|
- dev_name(eth->dev), eth);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
- err = mtk_mdio_init(eth);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
/* disable delay and normal interrupt */
|
|
|
mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
|
|
|
mtk_w32(eth, 0, MTK_PDMA_DELAY_INT);
|
|
@@ -1485,6 +1514,22 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int mtk_hw_deinit(struct mtk_eth *eth)
|
|
|
+{
|
|
|
+ if (!test_and_clear_bit(MTK_HW_INIT, ð->state))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ clk_disable_unprepare(eth->clks[MTK_CLK_GP2]);
|
|
|
+ clk_disable_unprepare(eth->clks[MTK_CLK_GP1]);
|
|
|
+ clk_disable_unprepare(eth->clks[MTK_CLK_ESW]);
|
|
|
+ clk_disable_unprepare(eth->clks[MTK_CLK_ETHIF]);
|
|
|
+
|
|
|
+ pm_runtime_put_sync(eth->dev);
|
|
|
+ pm_runtime_disable(eth->dev);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int __init mtk_init(struct net_device *dev)
|
|
|
{
|
|
|
struct mtk_mac *mac = netdev_priv(dev);
|
|
@@ -1543,6 +1588,12 @@ static void mtk_pending_work(struct work_struct *work)
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
+ dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__);
|
|
|
+
|
|
|
+ while (test_and_set_bit_lock(MTK_RESETTING, ð->state))
|
|
|
+ cpu_relax();
|
|
|
+
|
|
|
+ dev_dbg(eth->dev, "[%s][%d] mtk_stop starts\n", __func__, __LINE__);
|
|
|
/* stop all devices to make sure that dma is properly shut down */
|
|
|
for (i = 0; i < MTK_MAC_COUNT; i++) {
|
|
|
if (!eth->netdev[i])
|
|
@@ -1550,6 +1601,27 @@ static void mtk_pending_work(struct work_struct *work)
|
|
|
mtk_stop(eth->netdev[i]);
|
|
|
__set_bit(i, &restart);
|
|
|
}
|
|
|
+ dev_dbg(eth->dev, "[%s][%d] mtk_stop ends\n", __func__, __LINE__);
|
|
|
+
|
|
|
+ /* restart underlying hardware such as power, clock, pin mux
|
|
|
+ * and the connected phy
|
|
|
+ */
|
|
|
+ mtk_hw_deinit(eth);
|
|
|
+
|
|
|
+ if (eth->dev->pins)
|
|
|
+ pinctrl_select_state(eth->dev->pins->p,
|
|
|
+ eth->dev->pins->default_state);
|
|
|
+ mtk_hw_init(eth);
|
|
|
+
|
|
|
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
|
|
|
+ if (!eth->mac[i] ||
|
|
|
+ of_phy_is_fixed_link(eth->mac[i]->of_node))
|
|
|
+ continue;
|
|
|
+ err = phy_init_hw(eth->mac[i]->phy_dev);
|
|
|
+ if (err)
|
|
|
+ dev_err(eth->dev, "%s: PHY init failed.\n",
|
|
|
+ eth->netdev[i]->name);
|
|
|
+ }
|
|
|
|
|
|
/* restart DMA and enable IRQs */
|
|
|
for (i = 0; i < MTK_MAC_COUNT; i++) {
|
|
@@ -1562,20 +1634,44 @@ static void mtk_pending_work(struct work_struct *work)
|
|
|
dev_close(eth->netdev[i]);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__);
|
|
|
+
|
|
|
+ clear_bit_unlock(MTK_RESETTING, ð->state);
|
|
|
+
|
|
|
rtnl_unlock();
|
|
|
}
|
|
|
|
|
|
-static int mtk_cleanup(struct mtk_eth *eth)
|
|
|
+static int mtk_free_dev(struct mtk_eth *eth)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < MTK_MAC_COUNT; i++) {
|
|
|
if (!eth->netdev[i])
|
|
|
continue;
|
|
|
+ free_netdev(eth->netdev[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mtk_unreg_dev(struct mtk_eth *eth)
|
|
|
+{
|
|
|
+ int i;
|
|
|
|
|
|
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
|
|
|
+ if (!eth->netdev[i])
|
|
|
+ continue;
|
|
|
unregister_netdev(eth->netdev[i]);
|
|
|
- free_netdev(eth->netdev[i]);
|
|
|
}
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mtk_cleanup(struct mtk_eth *eth)
|
|
|
+{
|
|
|
+ mtk_unreg_dev(eth);
|
|
|
+ mtk_free_dev(eth);
|
|
|
cancel_work_sync(ð->pending_work);
|
|
|
|
|
|
return 0;
|
|
@@ -1587,6 +1683,9 @@ static int mtk_get_settings(struct net_device *dev,
|
|
|
struct mtk_mac *mac = netdev_priv(dev);
|
|
|
int err;
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
err = phy_read_status(mac->phy_dev);
|
|
|
if (err)
|
|
|
return -ENODEV;
|
|
@@ -1637,6 +1736,9 @@ static int mtk_nway_reset(struct net_device *dev)
|
|
|
{
|
|
|
struct mtk_mac *mac = netdev_priv(dev);
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
return genphy_restart_aneg(mac->phy_dev);
|
|
|
}
|
|
|
|
|
@@ -1645,6 +1747,9 @@ static u32 mtk_get_link(struct net_device *dev)
|
|
|
struct mtk_mac *mac = netdev_priv(dev);
|
|
|
int err;
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
err = genphy_update_link(mac->phy_dev);
|
|
|
if (err)
|
|
|
return ethtool_op_get_link(dev);
|
|
@@ -1685,6 +1790,9 @@ static void mtk_get_ethtool_stats(struct net_device *dev,
|
|
|
unsigned int start;
|
|
|
int i;
|
|
|
|
|
|
+ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
|
|
|
+ return;
|
|
|
+
|
|
|
if (netif_running(dev) && netif_device_present(dev)) {
|
|
|
if (spin_trylock(&hwstats->stats_lock)) {
|
|
|
mtk_stats_update_mac(mac);
|
|
@@ -1786,16 +1894,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
|
|
|
eth->netdev[id]->features |= MTK_HW_FEATURES;
|
|
|
eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
|
|
|
|
|
|
- err = register_netdev(eth->netdev[id]);
|
|
|
- if (err) {
|
|
|
- dev_err(eth->dev, "error bringing up device\n");
|
|
|
- goto free_netdev;
|
|
|
- }
|
|
|
eth->netdev[id]->irq = eth->irq[0];
|
|
|
- netif_info(eth, probe, eth->netdev[id],
|
|
|
- "mediatek frame engine at 0x%08lx, irq %d\n",
|
|
|
- eth->netdev[id]->base_addr, eth->irq[0]);
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
free_netdev:
|
|
@@ -1842,12 +1941,6 @@ static int mtk_probe(struct platform_device *pdev)
|
|
|
return PTR_ERR(eth->pctl);
|
|
|
}
|
|
|
|
|
|
- eth->rstc = devm_reset_control_get(&pdev->dev, "eth");
|
|
|
- if (IS_ERR(eth->rstc)) {
|
|
|
- dev_err(&pdev->dev, "no eth reset found\n");
|
|
|
- return PTR_ERR(eth->rstc);
|
|
|
- }
|
|
|
-
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
eth->irq[i] = platform_get_irq(pdev, i);
|
|
|
if (eth->irq[i] < 0) {
|
|
@@ -1865,11 +1958,6 @@ static int mtk_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- clk_prepare_enable(eth->clks[MTK_CLK_ETHIF]);
|
|
|
- clk_prepare_enable(eth->clks[MTK_CLK_ESW]);
|
|
|
- clk_prepare_enable(eth->clks[MTK_CLK_GP1]);
|
|
|
- clk_prepare_enable(eth->clks[MTK_CLK_GP2]);
|
|
|
-
|
|
|
eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
|
|
|
INIT_WORK(ð->pending_work, mtk_pending_work);
|
|
|
|
|
@@ -1887,7 +1975,35 @@ static int mtk_probe(struct platform_device *pdev)
|
|
|
|
|
|
err = mtk_add_mac(eth, mac_np);
|
|
|
if (err)
|
|
|
- goto err_free_dev;
|
|
|
+ goto err_deinit_hw;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
|
|
|
+ dev_name(eth->dev), eth);
|
|
|
+ if (err)
|
|
|
+ goto err_free_dev;
|
|
|
+
|
|
|
+ err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
|
|
|
+ dev_name(eth->dev), eth);
|
|
|
+ if (err)
|
|
|
+ goto err_free_dev;
|
|
|
+
|
|
|
+ err = mtk_mdio_init(eth);
|
|
|
+ if (err)
|
|
|
+ goto err_free_dev;
|
|
|
+
|
|
|
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
|
|
|
+ if (!eth->netdev[i])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ err = register_netdev(eth->netdev[i]);
|
|
|
+ if (err) {
|
|
|
+ dev_err(eth->dev, "error bringing up device\n");
|
|
|
+ goto err_deinit_mdio;
|
|
|
+ } else
|
|
|
+ netif_info(eth, probe, eth->netdev[i],
|
|
|
+ "mediatek frame engine at 0x%08lx, irq %d\n",
|
|
|
+ eth->netdev[i]->base_addr, eth->irq[0]);
|
|
|
}
|
|
|
|
|
|
/* we run 2 devices on the same DMA ring so we need a dummy device
|
|
@@ -1903,8 +2019,13 @@ static int mtk_probe(struct platform_device *pdev)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err_deinit_mdio:
|
|
|
+ mtk_mdio_cleanup(eth);
|
|
|
err_free_dev:
|
|
|
- mtk_cleanup(eth);
|
|
|
+ mtk_free_dev(eth);
|
|
|
+err_deinit_hw:
|
|
|
+ mtk_hw_deinit(eth);
|
|
|
+
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1920,10 +2041,7 @@ static int mtk_remove(struct platform_device *pdev)
|
|
|
mtk_stop(eth->netdev[i]);
|
|
|
}
|
|
|
|
|
|
- clk_disable_unprepare(eth->clks[MTK_CLK_ETHIF]);
|
|
|
- clk_disable_unprepare(eth->clks[MTK_CLK_ESW]);
|
|
|
- clk_disable_unprepare(eth->clks[MTK_CLK_GP1]);
|
|
|
- clk_disable_unprepare(eth->clks[MTK_CLK_GP2]);
|
|
|
+ mtk_hw_deinit(eth);
|
|
|
|
|
|
netif_napi_del(ð->tx_napi);
|
|
|
netif_napi_del(ð->rx_napi);
|