aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/renesas/rswitch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/renesas/rswitch.c')
-rw-r--r--drivers/net/ethernet/renesas/rswitch.c154
1 files changed, 124 insertions, 30 deletions
diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c
index 4e412ac0965a..e77c6ff93d81 100644
--- a/drivers/net/ethernet/renesas/rswitch.c
+++ b/drivers/net/ethernet/renesas/rswitch.c
@@ -4,6 +4,7 @@
* Copyright (C) 2022 Renesas Electronics Corporation
*/
+#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/etherdevice.h>
@@ -12,15 +13,16 @@
#include <linux/module.h>
#include <linux/net_tstamp.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/sys_soc.h>
#include "rswitch.h"
@@ -799,6 +801,7 @@ static int rswitch_poll(struct napi_struct *napi, int budget)
struct net_device *ndev = napi->dev;
struct rswitch_private *priv;
struct rswitch_device *rdev;
+ unsigned long flags;
int quota = budget;
rdev = netdev_priv(ndev);
@@ -816,10 +819,12 @@ retry:
netif_wake_subqueue(ndev, 0);
- napi_complete(napi);
-
- rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true);
- rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true);
+ if (napi_complete_done(napi, budget - quota)) {
+ spin_lock_irqsave(&priv->lock, flags);
+ rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true);
+ rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
out:
return budget - quota;
@@ -835,8 +840,10 @@ static void rswitch_queue_interrupt(struct net_device *ndev)
struct rswitch_device *rdev = netdev_priv(ndev);
if (napi_schedule_prep(&rdev->napi)) {
+ spin_lock(&rdev->priv->lock);
rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);
+ spin_unlock(&rdev->priv->lock);
__napi_schedule(&rdev->napi);
}
}
@@ -1044,7 +1051,7 @@ static void rswitch_rmac_setting(struct rswitch_etha *etha, const u8 *mac)
static void rswitch_etha_enable_mii(struct rswitch_etha *etha)
{
rswitch_modify(etha->addr, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK,
- MPIC_PSMCS(0x05) | MPIC_PSMHT(0x06));
+ MPIC_PSMCS(etha->psmcs) | MPIC_PSMHT(0x06));
rswitch_modify(etha->addr, MPSM, 0, MPSM_MFF_C45);
}
@@ -1244,22 +1251,31 @@ static void rswitch_adjust_link(struct net_device *ndev)
struct rswitch_device *rdev = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev;
- /* Current hardware has a restriction not to change speed at runtime */
if (phydev->link != rdev->etha->link) {
phy_print_status(phydev);
if (phydev->link)
phy_power_on(rdev->serdes);
- else
+ else if (rdev->serdes->power_count)
phy_power_off(rdev->serdes);
rdev->etha->link = phydev->link;
+
+ if (!rdev->priv->etha_no_runtime_change &&
+ phydev->speed != rdev->etha->speed) {
+ rdev->etha->speed = phydev->speed;
+
+ rswitch_etha_hw_init(rdev->etha, rdev->ndev->dev_addr);
+ phy_set_speed(rdev->serdes, rdev->etha->speed);
+ }
}
}
static void rswitch_phy_remove_link_mode(struct rswitch_device *rdev,
struct phy_device *phydev)
{
- /* Current hardware has a restriction not to change speed at runtime */
+ if (!rdev->priv->etha_no_runtime_change)
+ return;
+
switch (rdev->etha->speed) {
case SPEED_2500:
phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Full_BIT);
@@ -1300,6 +1316,7 @@ static int rswitch_phy_device_init(struct rswitch_device *rdev)
if (!phydev)
goto out;
__set_bit(rdev->etha->phy_interface, phydev->host_interfaces);
+ phydev->mac_managed_pm = true;
phydev = of_phy_connect(rdev->ndev, phy, rswitch_adjust_link, 0,
rdev->etha->phy_interface);
@@ -1348,7 +1365,8 @@ static int rswitch_ether_port_init_one(struct rswitch_device *rdev)
err = rswitch_etha_hw_init(rdev->etha, rdev->ndev->dev_addr);
if (err < 0)
return err;
- rdev->etha->operated = true;
+ if (rdev->priv->etha_no_runtime_change)
+ rdev->etha->operated = true;
}
err = rswitch_mii_register(rdev);
@@ -1389,7 +1407,8 @@ static void rswitch_ether_port_deinit_one(struct rswitch_device *rdev)
static int rswitch_ether_port_init_all(struct rswitch_private *priv)
{
- int i, err;
+ unsigned int i;
+ int err;
rswitch_for_each_enabled_port(priv, i) {
err = rswitch_ether_port_init_one(priv->rdev[i]);
@@ -1430,14 +1449,17 @@ static void rswitch_ether_port_deinit_all(struct rswitch_private *priv)
static int rswitch_open(struct net_device *ndev)
{
struct rswitch_device *rdev = netdev_priv(ndev);
+ unsigned long flags;
phy_start(ndev->phydev);
napi_enable(&rdev->napi);
netif_start_queue(ndev);
+ spin_lock_irqsave(&rdev->priv->lock, flags);
rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true);
rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true);
+ spin_unlock_irqrestore(&rdev->priv->lock, flags);
if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS))
iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE);
@@ -1451,6 +1473,7 @@ static int rswitch_stop(struct net_device *ndev)
{
struct rswitch_device *rdev = netdev_priv(ndev);
struct rswitch_gwca_ts_info *ts_info, *ts_info2;
+ unsigned long flags;
netif_tx_stop_all_queues(ndev);
bitmap_clear(rdev->priv->opened_ports, rdev->port, 1);
@@ -1466,8 +1489,10 @@ static int rswitch_stop(struct net_device *ndev)
kfree(ts_info);
}
+ spin_lock_irqsave(&rdev->priv->lock, flags);
rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);
+ spin_unlock_irqrestore(&rdev->priv->lock, flags);
phy_stop(ndev->phydev);
napi_disable(&rdev->napi);
@@ -1479,8 +1504,8 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
{
struct rswitch_device *rdev = netdev_priv(ndev);
struct rswitch_gwca_queue *gq = rdev->tx_queue;
+ netdev_tx_t ret = NETDEV_TX_OK;
struct rswitch_ext_desc *desc;
- int ret = NETDEV_TX_OK;
dma_addr_t dma_addr;
if (rswitch_get_num_cur_queues(gq) >= gq->ring_size - 1) {
@@ -1492,10 +1517,8 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
return ret;
dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len, DMA_TO_DEVICE);
- if (dma_mapping_error(ndev->dev.parent, dma_addr)) {
- dev_kfree_skb_any(skb);
- return ret;
- }
+ if (dma_mapping_error(ndev->dev.parent, dma_addr))
+ goto err_kfree;
gq->skbs[gq->cur] = skb;
desc = &gq->tx_ring[gq->cur];
@@ -1508,10 +1531,8 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
struct rswitch_gwca_ts_info *ts_info;
ts_info = kzalloc(sizeof(*ts_info), GFP_ATOMIC);
- if (!ts_info) {
- dma_unmap_single(ndev->dev.parent, dma_addr, skb->len, DMA_TO_DEVICE);
- return -ENOMEM;
- }
+ if (!ts_info)
+ goto err_unmap;
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
rdev->ts_tag++;
@@ -1534,6 +1555,14 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
rswitch_modify(rdev->addr, GWTRC(gq->index), 0, BIT(gq->index % 32));
return ret;
+
+err_unmap:
+ dma_unmap_single(ndev->dev.parent, dma_addr, skb->len, DMA_TO_DEVICE);
+
+err_kfree:
+ dev_kfree_skb_any(skb);
+
+ return ret;
}
static struct net_device_stats *rswitch_get_stats(struct net_device *ndev)
@@ -1654,6 +1683,8 @@ static int rswitch_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *
static const struct ethtool_ops rswitch_ethtool_ops = {
.get_ts_info = rswitch_get_ts_info,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
};
static const struct of_device_id renesas_eth_sw_of_table[] = {
@@ -1670,6 +1701,12 @@ static void rswitch_etha_init(struct rswitch_private *priv, int index)
etha->index = index;
etha->addr = priv->addr + RSWITCH_ETHA_OFFSET + index * RSWITCH_ETHA_SIZE;
etha->coma_addr = priv->addr;
+
+ /* MPIC.PSMCS = (clk [MHz] / (MDC frequency [MHz] * 2) - 1.
+ * Calculating PSMCS value as MDC frequency = 2.5MHz. So, multiply
+ * both the numerator and the denominator by 10.
+ */
+ etha->psmcs = clk_get_rate(priv->clk) / 100000 / (25 * 2) - 1;
}
static int rswitch_device_alloc(struct rswitch_private *priv, int index)
@@ -1756,7 +1793,8 @@ static void rswitch_device_free(struct rswitch_private *priv, int index)
static int rswitch_init(struct rswitch_private *priv)
{
- int i, err;
+ unsigned int i;
+ int err;
for (i = 0; i < RSWITCH_NUM_PORTS; i++)
rswitch_etha_init(priv, i);
@@ -1786,7 +1824,7 @@ static int rswitch_init(struct rswitch_private *priv)
for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
err = rswitch_device_alloc(priv, i);
if (err < 0) {
- for (i--; i >= 0; i--)
+ for (; i-- > 0; )
rswitch_device_free(priv, i);
goto err_device_alloc;
}
@@ -1854,8 +1892,14 @@ err_ts_queue_alloc:
return err;
}
+static const struct soc_device_attribute rswitch_soc_no_speed_change[] = {
+ { .soc_id = "r8a779f0", .revision = "ES1.0" },
+ { /* Sentinel */ }
+};
+
static int renesas_eth_sw_probe(struct platform_device *pdev)
{
+ const struct soc_device_attribute *attr;
struct rswitch_private *priv;
struct resource *res;
int ret;
@@ -1869,6 +1913,15 @@ static int renesas_eth_sw_probe(struct platform_device *pdev)
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ spin_lock_init(&priv->lock);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ attr = soc_device_match(rswitch_soc_no_speed_change);
+ if (attr)
+ priv->etha_no_runtime_change = true;
priv->ptp_priv = rcar_gen4_ptp_alloc(pdev);
if (!priv->ptp_priv)
@@ -1914,27 +1967,29 @@ static int renesas_eth_sw_probe(struct platform_device *pdev)
static void rswitch_deinit(struct rswitch_private *priv)
{
- int i;
+ unsigned int i;
rswitch_gwca_hw_deinit(priv);
rcar_gen4_ptp_unregister(priv->ptp_priv);
- for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
+ rswitch_for_each_enabled_port(priv, i) {
struct rswitch_device *rdev = priv->rdev[i];
- phy_exit(priv->rdev[i]->serdes);
- rswitch_ether_port_deinit_one(rdev);
unregister_netdev(rdev->ndev);
- rswitch_device_free(priv, i);
+ rswitch_ether_port_deinit_one(rdev);
+ phy_exit(priv->rdev[i]->serdes);
}
+ for (i = 0; i < RSWITCH_NUM_PORTS; i++)
+ rswitch_device_free(priv, i);
+
rswitch_gwca_ts_queue_free(priv);
rswitch_gwca_linkfix_free(priv);
rswitch_clock_disable(priv);
}
-static int renesas_eth_sw_remove(struct platform_device *pdev)
+static void renesas_eth_sw_remove(struct platform_device *pdev)
{
struct rswitch_private *priv = platform_get_drvdata(pdev);
@@ -1944,15 +1999,54 @@ static int renesas_eth_sw_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
+}
+
+static int renesas_eth_sw_suspend(struct device *dev)
+{
+ struct rswitch_private *priv = dev_get_drvdata(dev);
+ struct net_device *ndev;
+ unsigned int i;
+
+ rswitch_for_each_enabled_port(priv, i) {
+ ndev = priv->rdev[i]->ndev;
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ rswitch_stop(ndev);
+ }
+ if (priv->rdev[i]->serdes->init_count)
+ phy_exit(priv->rdev[i]->serdes);
+ }
+
+ return 0;
+}
+
+static int renesas_eth_sw_resume(struct device *dev)
+{
+ struct rswitch_private *priv = dev_get_drvdata(dev);
+ struct net_device *ndev;
+ unsigned int i;
+
+ rswitch_for_each_enabled_port(priv, i) {
+ phy_init(priv->rdev[i]->serdes);
+ ndev = priv->rdev[i]->ndev;
+ if (netif_running(ndev)) {
+ rswitch_open(ndev);
+ netif_device_attach(ndev);
+ }
+ }
return 0;
}
+static DEFINE_SIMPLE_DEV_PM_OPS(renesas_eth_sw_pm_ops, renesas_eth_sw_suspend,
+ renesas_eth_sw_resume);
+
static struct platform_driver renesas_eth_sw_driver_platform = {
.probe = renesas_eth_sw_probe,
- .remove = renesas_eth_sw_remove,
+ .remove_new = renesas_eth_sw_remove,
.driver = {
.name = "renesas_eth_sw",
+ .pm = pm_sleep_ptr(&renesas_eth_sw_pm_ops),
.of_match_table = renesas_eth_sw_of_table,
}
};