2023-09-26 11:55:05

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 00/13] Add Realtek automotive PCIe driver

This series includes adding realtek automotive ethernet driver
and adding rtase ethernet driver entry in MAINTAINERS file.

This ethernet device driver for the PCIe interface of
Realtek Automotive Ethernet Switch,applicable to
RTL9054, RTL9068, RTL9072, RTL9075, RTL9068, RTL9071.

v1 -> v2:
- Remove redundent debug message.
- Modify coding rule.
- Remove other function codes not related to netdev.

v2 -> v3:
- Remove SR-IOV function - We will add the SR-IOV function together when
uploading the vf driver in the future.
- Remove other unnecessary code and macro.

v3 -> v4:
- Remove function prototype - Our driver does not use recursion, so we
have reordered the code and removed the function prototypes.
- Define macro precisely - Improve macro code readability to make the
source code cleaner.

v4 -> v5:
- Modify ethtool function - Remove some unnecessary code.
- Don't use inline function - Let the compiler decide.

v5 -> v6:
- Some old macro definitions have been removed and replaced with the
lastest usage.
- Replace s32 with int to ensure consistency.
- Clearly point out the objects of the service and remove unnecessary
struct.

v6 -> v7:
- Split this driver into multiple patches.
- Reorganize this driver code and remove redundant code to make this
driver more concise.

v7 -> v8:
- Add the function to calculate time mitigation and the function to
calculate packet number mitigation. Users can use these two functions
to calculate the reg value that needs to be set for the mitigation value
they want to set.
- This device is usually used in automotive embedded systems. The page
pool api will use more memory in receiving packets and requires more
verification, so we currently do not plan to use it in this patch.

Justin Lai (13):
net:ethernet:realtek:rtase: Add pci table supported in this module
net:ethernet:realtek:rtase: Implement the .ndo_open function
net:ethernet:realtek:rtase: Implement the rtase_down function
net:ethernet:realtek:rtase: Implement the interrupt routine and
rtase_poll
net:ethernet:realtek:rtase: Implement hardware configuration function
net:ethernet:realtek:rtase: Implement .ndo_start_xmit function
net:ethernet:realtek:rtase: Implement a function to receive packets
net:ethernet:realtek:rtase: Implement net_device_ops
net:ethernet:realtek:rtase: Implement pci_driver suspend and resume
function
net:ethernet:realtek:rtase: Implement ethtool function
net:ethernet:realtek:rtase: Add a Makefile in the rtase folder
net:ethernet:realtek: Update the Makefile and Kconfig in the realtek
folder
MAINTAINERS: Add the rtase ethernet driver entry

MAINTAINERS | 7 +
drivers/net/ethernet/realtek/Kconfig | 17 +
drivers/net/ethernet/realtek/Makefile | 1 +
drivers/net/ethernet/realtek/rtase/Makefile | 10 +
drivers/net/ethernet/realtek/rtase/rtase.h | 341 +++
.../net/ethernet/realtek/rtase/rtase_main.c | 2492 +++++++++++++++++
6 files changed, 2868 insertions(+)
create mode 100644 drivers/net/ethernet/realtek/rtase/Makefile
create mode 100644 drivers/net/ethernet/realtek/rtase/rtase.h
create mode 100644 drivers/net/ethernet/realtek/rtase/rtase_main.c

--
2.34.1


2023-09-26 11:55:08

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 09/13] net:ethernet:realtek:rtase: Implement pci_driver suspend and resume function

Implement the pci_driver suspend function to enable the device
to sleep, and implement the resume function to enable the device
to resume operation.

Signed-off-by: Justin Lai <[email protected]>
---
.../net/ethernet/realtek/rtase/rtase_main.c | 61 +++++++++++++++++++
1 file changed, 61 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 8ea0fd15e884..9c6d8d69dcc5 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -2274,12 +2274,73 @@ static void rtase_shutdown(struct pci_dev *pdev)
rtase_reset_interrupt(pdev, tp);
}

+#ifdef CONFIG_PM
+static int rtase_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (!netif_running(dev))
+ goto out;
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+ netif_device_detach(dev);
+ rtase_hw_reset(dev);
+
+out:
+ pci_save_state(pdev);
+
+ return 0;
+}
+
+static int rtase_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct rtase_private *tp = netdev_priv(dev);
+ int ret;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ pci_enable_wake(pdev, PCI_D0, 0);
+
+ /* restore last modified mac address */
+ rtase_rar_set(tp, dev->dev_addr);
+
+ if (!netif_running(dev))
+ goto out;
+
+ rtase_wait_for_quiescence(dev);
+ netif_device_attach(dev);
+
+ rtase_tx_clear(tp);
+ rtase_rx_clear(tp);
+
+ ret = rtase_init_ring(dev);
+ if (ret)
+ netdev_alert(dev, "unable to init ring\n");
+
+ rtase_hw_config(dev);
+ /* always link, so start to transmit & receive */
+ rtase_hw_start(dev);
+
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+
+out:
+ return 0;
+}
+#endif /* CONFIG_PM */
+
static struct pci_driver rtase_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = rtase_pci_tbl,
.probe = rtase_init_one,
.remove = rtase_remove_one,
.shutdown = rtase_shutdown,
+#ifdef CONFIG_PM
+ .suspend = rtase_suspend,
+ .resume = rtase_resume,
+#endif
};

module_pci_driver(rtase_pci_driver);
--
2.34.1

2023-09-26 11:55:39

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 07/13] net:ethernet:realtek:rtase: Implement a function to receive packets

Implement rx_handler to read the information of the rx descriptor,
thereby checking the packet accordingly and storing the packet
in the socket buffer to complete the reception of the packet.

Signed-off-by: Justin Lai <[email protected]>
---
.../net/ethernet/realtek/rtase/rtase_main.c | 150 ++++++++++++++++++
1 file changed, 150 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 705cacd868b6..edd00a393bf1 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -464,6 +464,156 @@ static void rtase_rx_ring_clear(struct rtase_ring *ring)
}
}

+static int rtase_fragmented_frame(u32 status)
+{
+ return (status & (RX_FIRST_FRAG | RX_LAST_FRAG)) !=
+ (RX_FIRST_FRAG | RX_LAST_FRAG);
+}
+
+static void rtase_rx_csum(const struct rtase_private *tp, struct sk_buff *skb,
+ const union rx_desc *desc)
+{
+ u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
+
+ /* rx csum offload */
+ if (((opts2 & RX_V4F) && !(opts2 & RX_IPF)) || (opts2 & RX_V6F)) {
+ if (((opts2 & RX_TCPT) && !(opts2 & RX_TCPF)) ||
+ ((opts2 & RX_UDPT) && !(opts2 & RX_UDPF))) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else {
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+ } else {
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+}
+
+static void rtase_rx_vlan_skb(union rx_desc *desc, struct sk_buff *skb)
+{
+ u32 opts2 = le32_to_cpu(desc->desc_status.opts2);
+
+ if (!(opts2 & RX_VLAN_TAG))
+ return;
+
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & VLAN_TAG_MASK));
+}
+
+static void rtase_rx_skb(const struct rtase_ring *ring, struct sk_buff *skb)
+{
+ struct rtase_int_vector *ivec = ring->ivec;
+
+ napi_gro_receive(&ivec->napi, skb);
+}
+
+static int rx_handler(struct rtase_ring *ring, int budget)
+{
+ const struct rtase_private *tp = ring->ivec->tp;
+ u32 pkt_size, cur_rx, delta, entry, status;
+ struct net_device *dev = tp->dev;
+ union rx_desc *desc_base = ring->desc;
+ struct sk_buff *skb;
+ union rx_desc *desc;
+ int workdone = 0;
+
+ if (!ring->desc)
+ return workdone;
+
+ cur_rx = ring->cur_idx;
+ entry = cur_rx % NUM_DESC;
+ desc = &desc_base[entry];
+
+ do {
+ /* make sure discriptor has been updated */
+ rmb();
+ status = le32_to_cpu(desc->desc_status.opts1);
+
+ if (status & DESC_OWN)
+ break;
+
+ if (unlikely(status & RX_RES)) {
+ if (net_ratelimit())
+ netdev_warn(dev, "Rx ERROR. status = %08x\n",
+ status);
+
+ dev->stats.rx_errors++;
+
+ if (status & (RX_RWT | RX_RUNT))
+ dev->stats.rx_length_errors++;
+
+ if (status & RX_CRC)
+ dev->stats.rx_crc_errors++;
+
+ if (dev->features & NETIF_F_RXALL)
+ goto process_pkt;
+
+ rtase_mark_to_asic(desc, tp->rx_buf_sz);
+ goto skip_process_pkt;
+ }
+
+process_pkt:
+ pkt_size = status & RX_PKT_SIZE_MASK;
+ if (likely(!(dev->features & NETIF_F_RXFCS)))
+ pkt_size -= ETH_FCS_LEN;
+
+ /* the driver does not support incoming fragmented
+ * frames. they are seen as a symptom of over-mtu
+ * sized frames
+ */
+ if (unlikely(rtase_fragmented_frame(status))) {
+ dev->stats.rx_dropped++;
+ dev->stats.rx_length_errors++;
+ rtase_mark_to_asic(desc, tp->rx_buf_sz);
+ continue;
+ }
+
+ skb = ring->skbuff[entry];
+ dma_sync_single_for_cpu(&tp->pdev->dev,
+ ring->mis.data_phy_addr[entry],
+ tp->rx_buf_sz, DMA_FROM_DEVICE);
+
+ dma_unmap_single(&tp->pdev->dev,
+ ring->mis.data_phy_addr[entry], tp->rx_buf_sz,
+ DMA_FROM_DEVICE);
+ ring->skbuff[entry] = NULL;
+
+ if (dev->features & NETIF_F_RXCSUM)
+ rtase_rx_csum(tp, skb, desc);
+
+ skb->dev = dev;
+ skb_put(skb, pkt_size);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ if (skb->pkt_type == PACKET_MULTICAST)
+ dev->stats.multicast++;
+
+ rtase_rx_vlan_skb(desc, skb);
+ rtase_rx_skb(ring, skb);
+
+ dev->stats.rx_bytes += pkt_size;
+ dev->stats.rx_packets++;
+
+skip_process_pkt:
+ workdone++;
+ cur_rx++;
+ entry = cur_rx % NUM_DESC;
+ desc = ring->desc + sizeof(union rx_desc) * entry;
+ prefetch(desc);
+ } while (workdone != budget);
+
+ ring->cur_idx = cur_rx;
+ delta = rtase_rx_ring_fill(ring, ring->dirty_idx, ring->cur_idx, 1);
+
+ if (!delta && workdone)
+ netdev_info(dev, "no Rx buffer allocated\n");
+
+ ring->dirty_idx += delta;
+
+ if ((ring->dirty_idx + NUM_DESC) == ring->cur_idx)
+ netdev_emerg(dev, "Rx buffers exhausted\n");
+
+ return workdone;
+}
+
static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx)
{
struct rtase_ring *ring = &tp->rx_ring[idx];
--
2.34.1

2023-09-26 11:57:36

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 13/13] MAINTAINERS: Add the rtase ethernet driver entry

Add myself and Larry Chiu as the maintainer for the rtase ethernet driver.

Signed-off-by: Justin Lai <[email protected]>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 53b7ca804465..239aae94dc0f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18476,6 +18476,13 @@ L: [email protected]
S: Maintained
F: drivers/tty/rpmsg_tty.c

+RTASE ETHERNET DRIVER
+M: Justin Lai <[email protected]>
+M: Larry Chiu <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/net/ethernet/realtek/rtase/
+
RTL2830 MEDIA DRIVER
M: Antti Palosaari <[email protected]>
L: [email protected]
--
2.34.1

2023-09-26 12:00:08

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 08/13] net:ethernet:realtek:rtase: Implement net_device_ops

1. Implement .ndo_set_rx_mode so that the device can change address
list filtering.
2. Implement .ndo_set_mac_address so that mac address can be changed.
3. Implement .ndo_change_mtu so that mtu can be changed.
4. Implement .ndo_tx_timeout to perform related processing when the
transmitter does not make any progress.
5. Implement .ndo_get_stats64 to provide statistics that are called
when the user wants to get network device usage.
6. Implement .ndo_vlan_rx_add_vid to register VLAN ID when the device
supports VLAN filtering.
7. Implement .ndo_vlan_rx_kill_vid to unregister VLAN ID when the device
supports VLAN filtering.
8. Implement the .ndo_setup_tc to enable setting any "tc" scheduler,
classifier or action on dev.
9. Implement .ndo_fix_features enables adjusting requested feature flags
based on device-specific constraints.
10. Implement .ndo_set_features enables updating device configuration to
new features.

Signed-off-by: Justin Lai <[email protected]>
---
.../net/ethernet/realtek/rtase/rtase_main.c | 334 ++++++++++++++++++
1 file changed, 334 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index edd00a393bf1..8ea0fd15e884 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -1437,6 +1437,11 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
return NETDEV_TX_BUSY;
}

+static void rtase_set_rx_mode(struct net_device *dev)
+{
+ rtase_hw_set_rx_packet_filter(dev);
+}
+
static void rtase_enable_eem_write(const struct rtase_private *tp)
{
u8 val;
@@ -1469,6 +1474,240 @@ static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr)
rtase_w16(tp, RTASE_LBK_CTRL, LBK_ATLD | LBK_CLR);
}

+static int rtase_set_mac_address(struct net_device *dev, void *p)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ int ret;
+
+ ret = eth_mac_addr(dev, p);
+ if (ret)
+ return ret;
+
+ rtase_rar_set(tp, dev->dev_addr);
+
+ return 0;
+}
+
+static int rtase_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ int ret;
+
+ dev->mtu = new_mtu;
+
+ if (!netif_running(dev))
+ goto out;
+
+ rtase_down(dev);
+
+ rtase_set_rxbufsize(tp, dev);
+
+ ret = rtase_init_ring(dev);
+
+ if (ret)
+ return ret;
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+ rtase_hw_config(dev);
+
+ /* always link, so start to transmit & receive */
+ rtase_hw_start(dev);
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+
+out:
+ netdev_update_features(dev);
+
+ return ret;
+}
+
+static void rtase_wait_for_quiescence(const struct net_device *dev)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ struct rtase_int_vector *ivec;
+ u32 i;
+
+ for (i = 0; i < tp->int_nums; i++) {
+ ivec = &tp->int_vector[i];
+ synchronize_irq(ivec->irq);
+ /* wait for any pending NAPI task to complete */
+ napi_disable(&ivec->napi);
+ }
+
+ rtase_irq_dis_and_clear(tp);
+
+ for (i = 0; i < tp->int_nums; i++) {
+ ivec = &tp->int_vector[i];
+ napi_enable(&ivec->napi);
+ }
+}
+
+static void rtase_sw_reset(struct net_device *dev)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ int ret;
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+ rtase_hw_reset(dev);
+
+ /* let's wait a bit while any (async) irq lands on */
+ rtase_wait_for_quiescence(dev);
+ rtase_tx_clear(tp);
+ rtase_rx_clear(tp);
+
+ ret = rtase_init_ring(dev);
+ if (ret)
+ netdev_alert(dev, "unable to init ring\n");
+
+ rtase_hw_config(dev);
+ /* always link, so start to transmit & receive */
+ rtase_hw_start(dev);
+
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+}
+
+static void rtase_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+ rtase_sw_reset(dev);
+}
+
+static void rtase_dump_tally_counter(const struct rtase_private *tp,
+ dma_addr_t paddr)
+{
+ u32 cmd = lower_32_bits(le64_to_cpu(paddr));
+ u32 val;
+ int err;
+
+ rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(le64_to_cpu(paddr)));
+ rtase_w32(tp, RTASE_DTCCR0, cmd);
+ rtase_w32(tp, RTASE_DTCCR0, cmd | COUNTER_DUMP);
+
+ err = read_poll_timeout(rtase_r32, val, !(val & COUNTER_DUMP), 10, 250,
+ false, tp, RTASE_DTCCR0);
+
+ if (err == -ETIMEDOUT)
+ netdev_err(tp->dev, "error occurred in dump tally counter\n");
+}
+
+static void rtase_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ const struct rtase_private *tp = netdev_priv(dev);
+ const struct rtase_counters *counters = tp->tally_vaddr;
+ dma_addr_t paddr = tp->tally_paddr;
+
+ if (!counters)
+ return;
+
+ netdev_stats_to_stats64(stats, &dev->stats);
+ dev_fetch_sw_netstats(stats, dev->tstats);
+
+ /* fetch additional counter values missing in stats collected by driver
+ * from tally counter
+ */
+ rtase_dump_tally_counter(tp, paddr);
+
+ stats->tx_errors = le64_to_cpu(counters->tx_errors);
+ stats->collisions = le64_to_cpu(counters->tx_multi_collision);
+ stats->tx_aborted_errors = le64_to_cpu(counters->tx_aborted);
+ stats->rx_missed_errors = le64_to_cpu(counters->rx_missed);
+}
+
+static void rtase_enable_vlan_filter(const struct rtase_private *tp, bool enabled)
+{
+ u16 tmp;
+
+ if (enabled == 1) {
+ tmp = rtase_r16(tp, RTASE_FCR);
+ if (!(tmp & FCR_VLAN_FTR_EN))
+ rtase_w16(tp, RTASE_FCR, tmp | FCR_VLAN_FTR_EN);
+
+ tmp = rtase_r16(tp, RTASE_PCPR);
+ if (!(tmp & PCPR_VLAN_FTR_EN))
+ rtase_w16(tp, RTASE_PCPR, tmp | PCPR_VLAN_FTR_EN);
+ } else {
+ tmp = rtase_r16(tp, RTASE_FCR);
+ if (tmp & FCR_VLAN_FTR_EN)
+ rtase_w16(tp, RTASE_FCR, tmp & ~FCR_VLAN_FTR_EN);
+
+ tmp = rtase_r16(tp, RTASE_PCPR);
+ if (!(tmp & PCPR_VLAN_FTR_EN))
+ rtase_w16(tp, RTASE_PCPR, tmp & ~PCPR_VLAN_FTR_EN);
+ }
+}
+
+static int rtase_vlan_rx_add_vid(struct net_device *dev, __be16 protocol,
+ u16 vid)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ u16 tmp_mem, i;
+
+ if (be16_to_cpu(protocol) != ETH_P_8021Q)
+ return -EINVAL;
+
+ for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
+ u16 addr, mask;
+
+ if (!(tp->vlan_filter_ctrl & BIT(i))) {
+ tp->vlan_filter_ctrl |= BIT(i);
+ tp->vlan_filter_vid[i] = vid;
+ rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4,
+ vid | VLAN_ENTRY_CAREBIT);
+ /* each 16-bit register contains two VLAN entries */
+ addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
+ mask = 0x1 << ((i & 0x1) * 8);
+ tmp_mem = rtase_r16(tp, addr);
+ tmp_mem |= mask;
+ rtase_w16(tp, addr, tmp_mem);
+ break;
+ }
+ }
+
+ if (i == RTASE_VLAN_FILTER_ENTRY_NUM)
+ return -ENOENT;
+
+ rtase_enable_vlan_filter(tp, true);
+
+ return 0;
+}
+
+static int rtase_vlan_rx_kill_vid(struct net_device *dev, __be16 protocol,
+ u16 vid)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ u16 tmp_mem, i;
+
+ if (be16_to_cpu(protocol) != ETH_P_8021Q)
+ return -EINVAL;
+
+ for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) {
+ u16 addr, mask;
+
+ if (tp->vlan_filter_vid[i] == vid) {
+ tp->vlan_filter_ctrl &= ~BIT(i);
+ tp->vlan_filter_vid[i] = 0;
+ rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0);
+
+ /* each 16-bit register contains two VLAN entries */
+ addr = RTASE_VLAN_ENTRY_MEM_0 + (i & ~0x1);
+ mask = ~(0x1 << ((i & 0x1) * 8));
+ tmp_mem = rtase_r16(tp, addr);
+ tmp_mem &= mask;
+ rtase_w16(tp, addr, tmp_mem);
+ break;
+ }
+ }
+
+ /* check vlan filter enabled */
+ if (!tp->vlan_filter_ctrl)
+ rtase_enable_vlan_filter(tp, false);
+
+ return 0;
+}
+
#ifdef CONFIG_NET_POLL_CONTROLLER
/* Polling 'interrupt' - used by things like netconsole to send skbs
* without having to re-enable interrupts. It's not called while
@@ -1485,13 +1724,108 @@ static void rtase_netpoll(struct net_device *dev)
}
#endif

+static void rtase_set_hw_cbs(const struct rtase_private *tp, u32 queue)
+{
+ u32 idle = tp->tx_qos[queue].idleslope * RTASE_1T_CLOCK;
+ u32 val, i;
+
+ val = u32_encode_bits(idle / RTASE_1T_POWER, RTASE_IDLESLOPE_INT_MASK);
+ idle %= RTASE_1T_POWER;
+
+ for (i = 1; i <= RTASE_IDLESLOPE_INT_SHIFT; i++) {
+ idle *= 2;
+ if ((idle / RTASE_1T_POWER) == 1)
+ val |= BIT(RTASE_IDLESLOPE_INT_SHIFT - i);
+
+ idle %= RTASE_1T_POWER;
+ }
+ rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, val);
+}
+
+static int rtase_setup_tc_cbs(struct rtase_private *tp,
+ const struct tc_cbs_qopt_offload *qopt)
+{
+ u32 queue = qopt->queue;
+
+ tp->tx_qos[queue].hicredit = qopt->hicredit;
+ tp->tx_qos[queue].locredit = qopt->locredit;
+ tp->tx_qos[queue].idleslope = qopt->idleslope;
+ tp->tx_qos[queue].sendslope = qopt->sendslope;
+
+ /* set hardware cbs */
+ rtase_set_hw_cbs(tp, queue);
+
+ return 0;
+}
+
+static int rtase_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+{
+ struct rtase_private *tp = netdev_priv(dev);
+ int ret = 0;
+
+ switch (type) {
+ case TC_SETUP_QDISC_MQPRIO:
+ break;
+ case TC_SETUP_BLOCK:
+ break;
+ case TC_SETUP_QDISC_CBS:
+ ret = rtase_setup_tc_cbs(tp, type_data);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static netdev_features_t rtase_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ netdev_features_t features_fix = features;
+
+ if (dev->mtu > MSS_MAX)
+ features_fix &= ~NETIF_F_ALL_TSO;
+
+ if (dev->mtu > ETH_DATA_LEN) {
+ features_fix &= ~NETIF_F_ALL_TSO;
+ features_fix &= ~NETIF_F_CSUM_MASK;
+ }
+
+ return features_fix;
+}
+
+static int rtase_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ netdev_features_t features_set = features;
+
+ features_set &= NETIF_F_RXALL | NETIF_F_RXCSUM |
+ NETIF_F_HW_VLAN_CTAG_RX;
+
+ if (features_set ^ dev->features)
+ rtase_hw_set_features(dev, features_set);
+
+ return 0;
+}
+
static const struct net_device_ops rtase_netdev_ops = {
.ndo_open = rtase_open,
.ndo_stop = rtase_close,
.ndo_start_xmit = rtase_start_xmit,
+ .ndo_set_rx_mode = rtase_set_rx_mode,
+ .ndo_set_mac_address = rtase_set_mac_address,
+ .ndo_change_mtu = rtase_change_mtu,
+ .ndo_tx_timeout = rtase_tx_timeout,
+ .ndo_get_stats64 = rtase_get_stats64,
+ .ndo_vlan_rx_add_vid = rtase_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = rtase_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = rtase_netpoll,
#endif
+ .ndo_setup_tc = rtase_setup_tc,
+ .ndo_fix_features = rtase_fix_features,
+ .ndo_set_features = rtase_set_features,
};

static void rtase_get_mac_address(struct net_device *dev)
--
2.34.1

2023-09-26 12:00:11

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 11/13] net:ethernet:realtek:rtase: Add a Makefile in the rtase folder

Add a Makefile in the rtase folder to build rtase driver.

Signed-off-by: Justin Lai <[email protected]>
---
drivers/net/ethernet/realtek/rtase/Makefile | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 drivers/net/ethernet/realtek/rtase/Makefile

diff --git a/drivers/net/ethernet/realtek/rtase/Makefile b/drivers/net/ethernet/realtek/rtase/Makefile
new file mode 100644
index 000000000000..f29b14687950
--- /dev/null
+++ b/drivers/net/ethernet/realtek/rtase/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+# Copyright(c) 2023 Realtek Semiconductor Corp. All rights reserved.
+
+#
+# Makefile for the Realtek PCIe driver
+#
+
+obj-$(CONFIG_RTASE) += rtase.o
+
+rtase-objs := rtase_main.o
--
2.34.1

2023-09-26 12:11:21

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 06/13] net:ethernet:realtek:rtase: Implement .ndo_start_xmit function

Implement .ndo_start_xmit function to fill the information of the packet
to be transmitted into the tx descriptor, and then the hardware will
transmit the packet using the information in the tx descriptor.
In addition, we also implemented the tx_handler function to enable the
tx descriptor to be reused.

Signed-off-by: Justin Lai <[email protected]>
---
.../net/ethernet/realtek/rtase/rtase_main.c | 288 ++++++++++++++++++
1 file changed, 288 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 9b93cb578834..705cacd868b6 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -255,6 +255,68 @@ static void rtase_mark_to_asic(union rx_desc *desc, u32 rx_buf_sz)
cpu_to_le32(DESC_OWN | eor | rx_buf_sz));
}

+static bool rtase_tx_avail(struct rtase_ring *ring)
+{
+ u32 avail_num = READ_ONCE(ring->dirty_idx) + NUM_DESC -
+ READ_ONCE(ring->cur_idx);
+
+ return avail_num > MAX_SKB_FRAGS;
+}
+
+static int tx_handler(struct rtase_ring *ring, int budget)
+{
+ const struct rtase_private *tp = ring->ivec->tp;
+ struct net_device *dev = tp->dev;
+ int workdone = 0;
+ u32 dirty_tx;
+ u32 tx_left;
+
+ dirty_tx = ring->dirty_idx;
+ tx_left = READ_ONCE(ring->cur_idx) - dirty_tx;
+
+ while (tx_left > 0) {
+ u32 entry = dirty_tx % NUM_DESC;
+ struct tx_desc *desc = ring->desc +
+ sizeof(struct tx_desc) * entry;
+ u32 len = ring->mis.len[entry];
+ u32 status;
+
+ status = le32_to_cpu(desc->opts1);
+
+ if (status & DESC_OWN)
+ break;
+
+ rtase_unmap_tx_skb(tp->pdev, len, desc);
+ ring->mis.len[entry] = 0;
+ if (ring->skbuff[entry]) {
+ dev_consume_skb_any(ring->skbuff[entry]);
+ ring->skbuff[entry] = NULL;
+ }
+
+ dev->stats.tx_bytes += len;
+ dev->stats.tx_packets++;
+ dirty_tx++;
+ tx_left--;
+ workdone++;
+
+ if (workdone == budget)
+ break;
+ }
+
+ if (ring->dirty_idx != dirty_tx) {
+ WRITE_ONCE(ring->dirty_idx, dirty_tx);
+
+ if (__netif_subqueue_stopped(dev, ring->index) &&
+ rtase_tx_avail(ring))
+ netif_start_subqueue(dev, ring->index);
+
+ if (ring->cur_idx != dirty_tx)
+ rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
+ }
+
+ return workdone;
+}
+
static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx)
{
struct rtase_ring *ring = &tp->tx_ring[idx];
@@ -1000,6 +1062,231 @@ static int rtase_close(struct net_device *dev)
return 0;
}

+static u32 rtase_tx_vlan_tag(const struct rtase_private *tp,
+ const struct sk_buff *skb)
+{
+ return (skb_vlan_tag_present(skb)) ?
+ (TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb))) : 0x00;
+}
+
+static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
+{
+ u8 ip_protocol;
+ u32 csum_cmd;
+
+ switch (vlan_get_protocol(skb)) {
+ case htons(ETH_P_IP):
+ csum_cmd = TX_IPCS_C;
+ ip_protocol = ip_hdr(skb)->protocol;
+ break;
+
+ case htons(ETH_P_IPV6):
+ csum_cmd = TX_IPV6F_C;
+ ip_protocol = ipv6_hdr(skb)->nexthdr;
+ break;
+
+ default:
+ ip_protocol = IPPROTO_RAW;
+ break;
+ }
+
+ if (ip_protocol == IPPROTO_TCP)
+ csum_cmd |= TX_TCPCS_C;
+ else if (ip_protocol == IPPROTO_UDP)
+ csum_cmd |= TX_UDPCS_C;
+ else
+ WARN_ON_ONCE(1);
+
+ csum_cmd |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
+
+ return csum_cmd;
+}
+
+static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
+ u32 opts1, u32 opts2)
+{
+ const struct skb_shared_info *info = skb_shinfo(skb);
+ const struct rtase_private *tp = ring->ivec->tp;
+ const u8 nr_frags = info->nr_frags;
+ struct tx_desc *txd = NULL;
+ u32 cur_frag, entry;
+ u64 pkt_len_cnt = 0;
+
+ entry = ring->cur_idx;
+ for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
+ const skb_frag_t *frag = &info->frags[cur_frag];
+ dma_addr_t mapping;
+ u32 status, len;
+ void *addr;
+
+ entry = (entry + 1) % NUM_DESC;
+
+ txd = ring->desc + sizeof(struct tx_desc) * entry;
+ len = skb_frag_size(frag);
+ addr = skb_frag_address(frag);
+ mapping = dma_map_single(&tp->pdev->dev, addr, len,
+ DMA_TO_DEVICE);
+
+ if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
+ if (unlikely(net_ratelimit()))
+ netdev_err(tp->dev,
+ "Failed to map TX fragments DMA!\n");
+
+ goto err_out;
+ }
+
+ if (((entry + 1) % NUM_DESC) == 0)
+ status = (opts1 | len | RING_END);
+ else
+ status = opts1 | len;
+
+ if (cur_frag == (nr_frags - 1)) {
+ ring->skbuff[entry] = skb;
+ status |= TX_LAST_FRAG;
+ }
+
+ ring->mis.len[entry] = len;
+ txd->addr = cpu_to_le64(mapping);
+ txd->opts2 = cpu_to_le32(opts2);
+
+ /* make sure the operating fields have been updated */
+ wmb();
+ txd->opts1 = cpu_to_le32(status);
+ pkt_len_cnt += len;
+ }
+
+ return cur_frag;
+
+err_out:
+ rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag);
+ return -EIO;
+}
+
+static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ struct rtase_private *tp = netdev_priv(dev);
+ u32 q_idx, entry, len, opts1, opts2;
+ u32 mss = shinfo->gso_size;
+ struct rtase_ring *ring;
+ struct tx_desc *txd;
+ dma_addr_t mapping;
+ bool stop_queue;
+ int frags;
+
+ /* multiqueues */
+ q_idx = skb_get_queue_mapping(skb);
+ ring = &tp->tx_ring[q_idx];
+
+ if (unlikely(!rtase_tx_avail(ring))) {
+ if (net_ratelimit())
+ netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+ goto err_stop;
+ }
+
+ entry = ring->cur_idx % NUM_DESC;
+ txd = ring->desc + sizeof(struct tx_desc) * entry;
+
+ opts1 = DESC_OWN;
+ opts2 = rtase_tx_vlan_tag(tp, skb);
+
+ /* tcp segmentation offload (or tcp large send) */
+ if (mss) {
+ if (shinfo->gso_type & SKB_GSO_TCPV4) {
+ opts1 |= GIANT_SEND_V4;
+ } else if (shinfo->gso_type & SKB_GSO_TCPV6) {
+ if (skb_cow_head(skb, 0))
+ goto err_dma_0;
+
+ tcp_v6_gso_csum_prep(skb);
+ opts1 |= GIANT_SEND_V6;
+ } else {
+ WARN_ON_ONCE(1);
+ }
+
+ opts1 |= u32_encode_bits(skb_transport_offset(skb), TCPHO_MASK);
+ opts2 |= u32_encode_bits(mss, MSS_MASK);
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ opts2 |= rtase_tx_csum(skb, dev);
+ }
+
+ frags = rtase_xmit_frags(ring, skb, opts1, opts2);
+ if (unlikely(frags < 0))
+ goto err_dma_0;
+
+ if (frags) {
+ len = skb_headlen(skb);
+ opts1 |= TX_FIRST_FRAG;
+ } else {
+ len = skb->len;
+ ring->skbuff[entry] = skb;
+ opts1 |= TX_FIRST_FRAG | TX_LAST_FRAG;
+ }
+
+ if (((entry + 1) % NUM_DESC) == 0)
+ opts1 |= (len | RING_END);
+ else
+ opts1 |= len;
+
+ mapping = dma_map_single(&tp->pdev->dev, skb->data, len,
+ DMA_TO_DEVICE);
+
+ if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) {
+ if (unlikely(net_ratelimit()))
+ netdev_err(dev, "Failed to map TX DMA!\n");
+
+ goto err_dma_1;
+ }
+
+ ring->mis.len[entry] = len;
+ txd->addr = cpu_to_le64(mapping);
+ txd->opts2 = cpu_to_le32(opts2);
+ txd->opts1 = cpu_to_le32(opts1 & ~DESC_OWN);
+
+ /* make sure the operating fields have been updated */
+ wmb();
+
+ txd->opts1 = cpu_to_le32(opts1);
+
+ skb_tx_timestamp(skb);
+
+ /* tx needs to see descriptor changes before updated cur_idx */
+ smp_wmb();
+
+ WRITE_ONCE(ring->cur_idx, ring->cur_idx + frags + 1);
+
+ stop_queue = !rtase_tx_avail(ring);
+ if (unlikely(stop_queue))
+ netif_stop_subqueue(dev, q_idx);
+
+ /* set polling bit */
+ rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index));
+
+ if (unlikely(stop_queue)) {
+ /* make sure cur_idx and dirty_idx have been updated */
+ smp_rmb();
+ if (rtase_tx_avail(ring))
+ netif_start_subqueue(dev, q_idx);
+ }
+
+ return NETDEV_TX_OK;
+
+err_dma_1:
+ ring->skbuff[entry] = NULL;
+ rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
+
+err_dma_0:
+ dev->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+
+err_stop:
+ netif_stop_queue(dev);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+}
+
static void rtase_enable_eem_write(const struct rtase_private *tp)
{
u8 val;
@@ -1051,6 +1338,7 @@ static void rtase_netpoll(struct net_device *dev)
static const struct net_device_ops rtase_netdev_ops = {
.ndo_open = rtase_open,
.ndo_stop = rtase_close,
+ .ndo_start_xmit = rtase_start_xmit,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = rtase_netpoll,
#endif
--
2.34.1

2023-09-26 12:13:40

by Justin Lai

[permalink] [raw]
Subject: [PATCH net-next v8 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder

1. Add the RTASE entry in the Kconfig.
2. Add the CONFIG_RTASE entry in the Makefile.

Signed-off-by: Justin Lai <[email protected]>
---
drivers/net/ethernet/realtek/Kconfig | 17 +++++++++++++++++
drivers/net/ethernet/realtek/Makefile | 1 +
2 files changed, 18 insertions(+)

diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 93d9df55b361..57ef924deebd 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -113,4 +113,21 @@ config R8169
To compile this driver as a module, choose M here: the module
will be called r8169. This is recommended.

+config RTASE
+ tristate "Realtek Automotive Switch 9054/9068/9072/9075/9068/9071 PCIe Interface support"
+ depends on PCI
+ select CRC32
+ help
+ Say Y here if you have a Realtek Ethernet adapter belonging to
+ the following families:
+ RTL9054 5GBit Ethernet
+ RTL9068 5GBit Ethernet
+ RTL9072 5GBit Ethernet
+ RTL9075 5GBit Ethernet
+ RTL9068 5GBit Ethernet
+ RTL9071 5GBit Ethernet
+
+ To compile this driver as a module, choose M here: the module
+ will be called rtase. This is recommended.
+
endif # NET_VENDOR_REALTEK
diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile
index 2e1d78b106b0..0c1c16f63e9a 100644
--- a/drivers/net/ethernet/realtek/Makefile
+++ b/drivers/net/ethernet/realtek/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_8139TOO) += 8139too.o
obj-$(CONFIG_ATP) += atp.o
r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
obj-$(CONFIG_R8169) += r8169.o
+obj-$(CONFIG_RTASE) += rtase/
--
2.34.1

2023-09-26 23:12:29

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH net-next v8 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder

Hi Justin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url: https://github.com/intel-lab-lkp/linux/commits/Justin-Lai/net-ethernet-realtek-rtase-Add-pci-table-supported-in-this-module/20230926-195218
base: net-next/main
patch link: https://lore.kernel.org/r/20230926114943.16340-13-justinlai0215%40realtek.com
patch subject: [PATCH net-next v8 12/13] net:ethernet:realtek: Update the Makefile and Kconfig in the realtek folder
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20230927/[email protected]/config)
compiler: alpha-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230927/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> drivers/net/ethernet/realtek/rtase/rtase_main.c:234:6: warning: no previous prototype for 'rtase_tx_clear' [-Wmissing-prototypes]
234 | void rtase_tx_clear(struct rtase_private *tp)
| ^~~~~~~~~~~~~~
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:636:6: warning: no previous prototype for 'rtase_rx_clear' [-Wmissing-prototypes]
636 | void rtase_rx_clear(struct rtase_private *tp)
| ^~~~~~~~~~~~~~
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:776:6: warning: no previous prototype for 'rtase_hw_set_rx_packet_filter' [-Wmissing-prototypes]
776 | void rtase_hw_set_rx_packet_filter(struct net_device *dev)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:879:6: warning: no previous prototype for 'rtase_hw_reset' [-Wmissing-prototypes]
879 | void rtase_hw_reset(const struct net_device *dev)
| ^~~~~~~~~~~~~~
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:1027:6: warning: no previous prototype for 'rtase_hw_start' [-Wmissing-prototypes]
1027 | void rtase_hw_start(const struct net_device *dev)
| ^~~~~~~~~~~~~~
drivers/net/ethernet/realtek/rtase/rtase_main.c: In function 'rtase_open':
>> drivers/net/ethernet/realtek/rtase/rtase_main.c:1140:25: warning: 'sprintf' argument 3 may overlap destination object 'dev' [-Wrestrict]
1140 | sprintf((char *)&ivec->name, "%s_int%i", dev->name, i);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/realtek/rtase/rtase_main.c:1105:42: note: destination object referenced by 'restrict'-qualified argument 1 was declared here
1105 | static int rtase_open(struct net_device *dev)
| ~~~~~~~~~~~~~~~~~~~^~~


vim +/rtase_tx_clear +234 drivers/net/ethernet/realtek/rtase/rtase_main.c

9a15ffa44557f94 Justin Lai 2023-09-26 233
9a15ffa44557f94 Justin Lai 2023-09-26 @234 void rtase_tx_clear(struct rtase_private *tp)
9a15ffa44557f94 Justin Lai 2023-09-26 235 {
9a15ffa44557f94 Justin Lai 2023-09-26 236 struct rtase_ring *ring;
9a15ffa44557f94 Justin Lai 2023-09-26 237 u16 i;
9a15ffa44557f94 Justin Lai 2023-09-26 238
9a15ffa44557f94 Justin Lai 2023-09-26 239 for (i = 0; i < tp->func_tx_queue_num; i++) {
9a15ffa44557f94 Justin Lai 2023-09-26 240 ring = &tp->tx_ring[i];
9a15ffa44557f94 Justin Lai 2023-09-26 241 rtase_tx_clear_range(ring, ring->dirty_idx, NUM_DESC);
9a15ffa44557f94 Justin Lai 2023-09-26 242 ring->cur_idx = 0;
9a15ffa44557f94 Justin Lai 2023-09-26 243 ring->dirty_idx = 0;
9a15ffa44557f94 Justin Lai 2023-09-26 244 }
9a15ffa44557f94 Justin Lai 2023-09-26 245 }
9a15ffa44557f94 Justin Lai 2023-09-26 246

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki