From 9999e48639b3cecb08ffb37358bcba3b48161b29 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 08:50:17 +0000
Subject: [PATCH] add ax88772_rst

---
 kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_uio.c |  432 +++++++++++++++++++-----------------------------------
 1 files changed, 152 insertions(+), 280 deletions(-)

diff --git a/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_uio.c b/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_uio.c
index b241bd9..11ec4b7 100644
--- a/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_uio.c
+++ b/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_uio.c
@@ -16,7 +16,6 @@
 #include <linux/interrupt.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
-#include <linux/skbuff.h>
 #include <linux/ethtool.h>
 #include <linux/if_ether.h>
 #include <linux/crc32.h>
@@ -45,14 +44,14 @@
 
 #define DRIVER_NAME	"rockchip_gmac_uio_drv"
 #define DRIVER_VERSION	"0.1"
-#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
 
 #define TC_DEFAULT 64
-#define DEFAULT_BUFSIZE	1536
-#define STMMAC_RX_COPYBREAK	256
-
-static int buf_sz = DEFAULT_BUFSIZE;
 static int tc = TC_DEFAULT;
+
+#define DEFAULT_BUFSIZE	1536
+static int buf_sz = DEFAULT_BUFSIZE;
+
+#define STMMAC_RX_COPYBREAK	256
 
 /**
  * rockchip_gmac_uio_pdev_info
@@ -120,11 +119,11 @@
 
 		/* Free DMA regions of consistent memory previously allocated */
 		if (!priv->extend_desc)
-			dma_free_coherent(priv->device,
-					  DMA_RX_SIZE * sizeof(struct dma_desc),
+			dma_free_coherent(priv->device, priv->dma_rx_size *
+					  sizeof(struct dma_desc),
 					  rx_q->dma_rx, rx_q->dma_rx_phy);
 		else
-			dma_free_coherent(priv->device, DMA_RX_SIZE *
+			dma_free_coherent(priv->device, priv->dma_rx_size *
 					  sizeof(struct dma_extended_desc),
 					  rx_q->dma_erx, rx_q->dma_rx_phy);
 	}
@@ -142,16 +141,23 @@
 	/* Free TX queue resources */
 	for (queue = 0; queue < tx_count; queue++) {
 		struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+		size_t size;
+		void *addr;
 
-		/* Free DMA regions of consistent memory previously allocated */
-		if (!priv->extend_desc)
-			dma_free_coherent(priv->device,
-					  DMA_TX_SIZE * sizeof(struct dma_desc),
-					  tx_q->dma_tx, tx_q->dma_tx_phy);
-		else
-			dma_free_coherent(priv->device, DMA_TX_SIZE *
-					  sizeof(struct dma_extended_desc),
-					  tx_q->dma_etx, tx_q->dma_tx_phy);
+		if (priv->extend_desc) {
+			size = sizeof(struct dma_extended_desc);
+			addr = tx_q->dma_etx;
+		} else if (tx_q->tbs & STMMAC_TBS_AVAIL) {
+			size = sizeof(struct dma_edesc);
+			addr = tx_q->dma_entx;
+		} else {
+			size = sizeof(struct dma_desc);
+			addr = tx_q->dma_tx;
+		}
+
+		size *= priv->dma_tx_size;
+
+		dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
 	}
 }
 
@@ -173,26 +179,20 @@
 	for (queue = 0; queue < rx_count; queue++) {
 		struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
 
-		rx_q->queue_index = queue;
-		rx_q->priv_data = priv;
-
 		if (priv->extend_desc) {
-			rx_q->dma_erx = dma_zalloc_coherent(priv->device,
-							    DMA_RX_SIZE *
-							    sizeof(struct
-							    dma_extended_desc),
-							    &rx_q->dma_rx_phy,
-							    GFP_KERNEL);
-			if (!rx_q->dma_erx)
-				goto err_dma;
-
-		} else {
-			rx_q->dma_rx = dma_zalloc_coherent(priv->device,
-							   DMA_RX_SIZE *
-							   sizeof(struct
-							   dma_desc),
+			rx_q->dma_erx = dma_alloc_coherent(priv->device,
+							   priv->dma_rx_size *
+							   sizeof(struct dma_extended_desc),
 							   &rx_q->dma_rx_phy,
 							   GFP_KERNEL);
+			if (!rx_q->dma_erx)
+				goto err_dma;
+		} else {
+			rx_q->dma_rx = dma_alloc_coherent(priv->device,
+							  priv->dma_rx_size *
+							  sizeof(struct dma_desc),
+							  &rx_q->dma_rx_phy,
+							  GFP_KERNEL);
 			if (!rx_q->dma_rx)
 				goto err_dma;
 		}
@@ -223,36 +223,38 @@
 	/* TX queues buffers and DMA */
 	for (queue = 0; queue < tx_count; queue++) {
 		struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+		size_t size;
+		void *addr;
 
 		tx_q->queue_index = queue;
 		tx_q->priv_data = priv;
 
-		if (priv->extend_desc) {
-			tx_q->dma_etx = dma_zalloc_coherent(priv->device,
-							    DMA_TX_SIZE *
-							    sizeof(struct
-							    dma_extended_desc),
-							    &tx_q->dma_tx_phy,
-							    GFP_KERNEL);
-			if (!tx_q->dma_etx)
-				goto err_dma;
-		} else {
-			tx_q->dma_tx = dma_zalloc_coherent(priv->device,
-							   DMA_TX_SIZE *
-							   sizeof(struct
-								  dma_desc),
-							   &tx_q->dma_tx_phy,
-							   GFP_KERNEL);
-			if (!tx_q->dma_tx)
-				goto err_dma;
-		}
+		if (priv->extend_desc)
+			size = sizeof(struct dma_extended_desc);
+		else if (tx_q->tbs & STMMAC_TBS_AVAIL)
+			size = sizeof(struct dma_edesc);
+		else
+			size = sizeof(struct dma_desc);
+
+		size *= priv->dma_tx_size;
+
+		addr = dma_alloc_coherent(priv->device, size,
+					  &tx_q->dma_tx_phy, GFP_KERNEL);
+		if (!addr)
+			goto err_dma;
+
+		if (priv->extend_desc)
+			tx_q->dma_etx = addr;
+		else if (tx_q->tbs & STMMAC_TBS_AVAIL)
+			tx_q->dma_entx = addr;
+		else
+			tx_q->dma_tx = addr;
 	}
 
 	return 0;
 
 err_dma:
 	uio_free_dma_tx_desc_resources(priv);
-
 	return ret;
 }
 
@@ -291,121 +293,6 @@
 }
 
 /**
- * uio_hw_fix_mac_speed - callback for speed selection
- * @priv: driver private structure
- * Description: on some platforms (e.g. ST), some HW system configuration
- * registers have to be set according to the link speed negotiated.
- */
-static inline void uio_hw_fix_mac_speed(struct stmmac_priv *priv)
-{
-	struct net_device *ndev = priv->dev;
-	struct phy_device *phydev = ndev->phydev;
-
-	if (likely(priv->plat->fix_mac_speed))
-		priv->plat->fix_mac_speed(priv->plat->bsp_priv, phydev->speed);
-}
-
-/**
- *  uio_mac_flow_ctrl - Configure flow control in all queues
- *  @priv: driver private structure
- *  Description: It is used for configuring the flow control in all queues
- */
-static void uio_mac_flow_ctrl(struct stmmac_priv *priv, u32 duplex)
-{
-	u32 tx_cnt = priv->plat->tx_queues_to_use;
-
-	stmmac_flow_ctrl(priv, priv->hw, duplex, priv->flow_ctrl,
-			 priv->pause, tx_cnt);
-}
-
-/**
- * uio_adjust_link - adjusts the link parameters
- * @dev: net device structure
- * Description: this is the helper called by the physical abstraction layer
- * drivers to communicate the phy link status. According the speed and duplex
- * this driver can invoke registered glue-logic as well.
- * It also invoke the eee initialization because it could happen when switch
- * on different networks (that are eee capable).
- */
-static void uio_adjust_link(struct net_device *dev)
-{
-	struct stmmac_priv *priv = netdev_priv(dev);
-	struct phy_device *phydev = dev->phydev;
-	bool new_state = false;
-
-	if (!phydev)
-		return;
-
-	mutex_lock(&priv->lock);
-
-	if (phydev->link) {
-		u32 ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
-
-		/* Now we make sure that we can be in full duplex mode.
-		 * If not, we operate in half-duplex mode.
-		 */
-		if (phydev->duplex != priv->oldduplex) {
-			new_state = true;
-			if (!phydev->duplex)
-				ctrl &= ~priv->hw->link.duplex;
-			else
-				ctrl |= priv->hw->link.duplex;
-			priv->oldduplex = phydev->duplex;
-		}
-		/* Flow Control operation */
-		if (phydev->pause)
-			uio_mac_flow_ctrl(priv, phydev->duplex);
-
-		if (phydev->speed != priv->speed) {
-			new_state = true;
-			ctrl &= ~priv->hw->link.speed_mask;
-			switch (phydev->speed) {
-			case SPEED_1000:
-				ctrl |= priv->hw->link.speed1000;
-				break;
-			case SPEED_100:
-				ctrl |= priv->hw->link.speed100;
-				break;
-			case SPEED_10:
-				ctrl |= priv->hw->link.speed10;
-				break;
-			default:
-				netif_warn(priv, link, priv->dev,
-					   "broken speed: %d\n", phydev->speed);
-				phydev->speed = SPEED_UNKNOWN;
-				break;
-			}
-			if (phydev->speed != SPEED_UNKNOWN)
-				uio_hw_fix_mac_speed(priv);
-			priv->speed = phydev->speed;
-		}
-
-		writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
-
-		if (!priv->oldlink) {
-			new_state = true;
-			priv->oldlink = true;
-		}
-	} else if (priv->oldlink) {
-		new_state = true;
-		priv->oldlink = false;
-		priv->speed = SPEED_UNKNOWN;
-		priv->oldduplex = DUPLEX_UNKNOWN;
-	}
-
-	if (new_state && netif_msg_link(priv))
-		phy_print_status(phydev);
-
-	mutex_unlock(&priv->lock);
-
-	if (phydev->is_pseudo_fixed_link)
-		/* Stop PHY layer to call the hook to adjust the link in case
-		 * of a switch is attached to the stmmac driver.
-		 */
-		phydev->irq = PHY_IGNORE_INTERRUPT;
-}
-
-/**
  * rockchip_gmac_uio_init_phy - PHY initialization
  * @dev: net device structure
  * Description: it initializes the driver's PHY state, and attaches the PHY
@@ -416,82 +303,38 @@
 static int rockchip_gmac_uio_init_phy(struct net_device *dev)
 {
 	struct stmmac_priv *priv = netdev_priv(dev);
-	u32 tx_cnt = priv->plat->tx_queues_to_use;
-	struct phy_device *phydev;
-	char phy_id_fmt[MII_BUS_ID_SIZE + 3];
-	char bus_id[MII_BUS_ID_SIZE];
-	int interface = priv->plat->interface;
-	int max_speed = priv->plat->max_speed;
+	struct device_node *node;
+	int ret;
 
-	priv->oldlink = false;
-	priv->speed = SPEED_UNKNOWN;
-	priv->oldduplex = DUPLEX_UNKNOWN;
+	node = priv->plat->phylink_node;
 
-	if (priv->plat->integrated_phy_power)
-		priv->plat->integrated_phy_power(priv->plat->bsp_priv, true);
+	if (node)
+		ret = phylink_of_phy_connect(priv->phylink, node, 0);
 
-	if (priv->mii)
-		stmmac_mdio_reset(priv->mii);
+	/* Some DT bindings do not set-up the PHY handle. Let's try to
+	 * manually parse it
+	 */
+	if (!node || ret) {
+		int addr = priv->plat->phy_addr;
+		struct phy_device *phydev;
 
-	if (priv->plat->phy_node) {
-		phydev = of_phy_connect(dev, priv->plat->phy_node,
-					&uio_adjust_link, 0, interface);
-	} else {
-		snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
-			 priv->plat->bus_id);
-
-		snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
-			 priv->plat->phy_addr);
-		netdev_dbg(priv->dev, "%s: trying to attach to %s\n", __func__,
-			   phy_id_fmt);
-
-		phydev = phy_connect(dev, phy_id_fmt, &uio_adjust_link,
-				     interface);
-	}
-
-	if (IS_ERR_OR_NULL(phydev)) {
-		netdev_err(priv->dev, "Could not attach to PHY\n");
-		if (!phydev)
+		phydev = mdiobus_get_phy(priv->mii, addr);
+		if (!phydev) {
+			netdev_err(priv->dev, "no phy at addr %d\n", addr);
 			return -ENODEV;
+		}
 
-		return PTR_ERR(phydev);
+		ret = phylink_connect_phy(priv->phylink, phydev);
 	}
 
-	/* Stop Advertising 1000BASE Capability if interface is not GMII */
-	if (interface == PHY_INTERFACE_MODE_MII ||
-	    interface == PHY_INTERFACE_MODE_RMII ||
-		(max_speed < 1000 && max_speed > 0))
-		phydev->advertising &= ~(SUPPORTED_1000baseT_Half |
-					 SUPPORTED_1000baseT_Full);
+	if (!priv->plat->pmt) {
+		struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
 
-	/* Half-duplex mode not supported with multiqueue
-	 * half-duplex can only works with single queue
-	 */
-	if (tx_cnt > 1)
-		phydev->supported &= ~(SUPPORTED_1000baseT_Half |
-				       SUPPORTED_100baseT_Half |
-				       SUPPORTED_10baseT_Half);
-
-	/* Broken HW is sometimes missing the pull-up resistor on the
-	 * MDIO line, which results in reads to non-existent devices returning
-	 * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent
-	 * device as well.
-	 * Note: phydev->phy_id is the result of reading the UID PHY registers.
-	 */
-	if (!priv->plat->phy_node && phydev->phy_id == 0) {
-		phy_disconnect(phydev);
-		return -ENODEV;
+		phylink_ethtool_get_wol(priv->phylink, &wol);
+		device_set_wakeup_capable(priv->device, !!wol.supported);
 	}
 
-	/* uio_adjust_link will change this to PHY_IGNORE_INTERRUPT to avoid
-	 * subsequent PHY polling, make sure we force a link transition if
-	 * we have a UP/DOWN/UP transition
-	 */
-	if (phydev->is_pseudo_fixed_link)
-		phydev->irq = PHY_POLL;
-
-	phy_attached_info(phydev);
-	return 0;
+	return ret;
 }
 
 /**
@@ -545,7 +388,8 @@
 				    rx_q->dma_rx_phy, chan);
 
 		rx_q->rx_tail_addr = rx_q->dma_rx_phy +
-			    (DMA_RX_SIZE * sizeof(struct dma_desc));
+				     (priv->dma_rx_size *
+				      sizeof(struct dma_desc));
 		stmmac_set_rx_tail_ptr(priv, priv->ioaddr,
 				       rx_q->rx_tail_addr, chan);
 	}
@@ -574,12 +418,12 @@
 	/* set TX ring length */
 	for (chan = 0; chan < tx_channels_count; chan++)
 		stmmac_set_tx_ring_len(priv, priv->ioaddr,
-				       (DMA_TX_SIZE - 1), chan);
+				       (priv->dma_tx_size - 1), chan);
 
 	/* set RX ring length */
 	for (chan = 0; chan < rx_channels_count; chan++)
 		stmmac_set_rx_ring_len(priv, priv->ioaddr,
-				       (DMA_RX_SIZE - 1), chan);
+				       (priv->dma_rx_size - 1), chan);
 }
 
 /**
@@ -617,11 +461,11 @@
 			continue;
 
 		stmmac_config_cbs(priv, priv->hw,
-				priv->plat->tx_queues_cfg[queue].send_slope,
-				priv->plat->tx_queues_cfg[queue].idle_slope,
-				priv->plat->tx_queues_cfg[queue].high_credit,
-				priv->plat->tx_queues_cfg[queue].low_credit,
-				queue);
+				  priv->plat->tx_queues_cfg[queue].send_slope,
+				  priv->plat->tx_queues_cfg[queue].idle_slope,
+				  priv->plat->tx_queues_cfg[queue].high_credit,
+				  priv->plat->tx_queues_cfg[queue].low_credit,
+				  queue);
 	}
 }
 
@@ -703,6 +547,22 @@
 	}
 }
 
+static void uio_mac_config_rss(struct stmmac_priv *priv)
+{
+	if (!priv->dma_cap.rssen || !priv->plat->rss_en) {
+		priv->rss.enable = false;
+		return;
+	}
+
+	if (priv->dev->features & NETIF_F_RXHASH)
+		priv->rss.enable = true;
+	else
+		priv->rss.enable = false;
+
+	stmmac_rss_configure(priv, priv->hw, &priv->rss,
+			     priv->plat->rx_queues_to_use);
+}
+
 /**
  *  uio_mac_enable_rx_queues - Enable MAC rx queues
  *  @priv: driver private structure
@@ -764,14 +624,17 @@
 	/* Set RX routing */
 	if (rx_queues_count > 1)
 		uio_mac_config_rx_queues_routing(priv);
+
+	/* Receive Side Scaling */
+	if (rx_queues_count > 1)
+		uio_mac_config_rss(priv);
 }
 
 static void uio_safety_feat_configuration(struct stmmac_priv *priv)
 {
 	if (priv->dma_cap.asp) {
 		netdev_info(priv->dev, "Enabling Safety Features\n");
-		stmmac_safety_feat_config(priv, priv->ioaddr,
-					  priv->dma_cap.asp);
+		stmmac_safety_feat_config(priv, priv->ioaddr, priv->dma_cap.asp);
 	} else {
 		netdev_info(priv->dev, "No Safety Features support found\n");
 	}
@@ -840,26 +703,6 @@
 }
 
 /**
- * rockchip_gmac_uio_mmc_setup: setup the Mac Management Counters (MMC)
- * @priv: driver private structure
- * Description: this masks the MMC irq, in fact, the counters are managed in SW.
- */
-static void rockchip_gmac_uio_mmc_setup(struct stmmac_priv *priv)
-{
-	unsigned int mode = MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET |
-			    MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
-
-	dwmac_mmc_intr_all_mask(priv->mmcaddr);
-
-	if (priv->dma_cap.rmon) {
-		dwmac_mmc_ctrl(priv->mmcaddr, mode);
-		memset(&priv->mmc, 0, sizeof(struct stmmac_counters));
-	} else {
-		netdev_info(priv->dev, "No MAC Management Counters available\n");
-	}
-}
-
-/**
  *  rockchip_gmac_uio_hw_setup - setup mac in a usable state.
  *  @dev : pointer to the device structure.
  *  @init_ptp: initialize PTP if set
@@ -923,13 +766,29 @@
 	/* Set the HW DMA mode and the COE */
 	uio_dma_operation_mode(priv);
 
-	rockchip_gmac_uio_mmc_setup(priv);
-
 	if (priv->hw->pcs)
 		stmmac_pcs_ctrl_ane(priv, priv->hw, 1, priv->hw->ps, 0);
 
 	/* set TX and RX rings length */
 	uio_set_rings_length(priv);
+
+	return 0;
+}
+
+static int uio_set_bfsize(int mtu, int bufsize)
+{
+	int ret = bufsize;
+
+	if (mtu >= BUF_SIZE_8KiB)
+		ret = BUF_SIZE_16KiB;
+	else if (mtu >= BUF_SIZE_4KiB)
+		ret = BUF_SIZE_8KiB;
+	else if (mtu >= BUF_SIZE_2KiB)
+		ret = BUF_SIZE_4KiB;
+	else if (mtu > DEFAULT_BUFSIZE)
+		ret = BUF_SIZE_2KiB;
+	else
+		ret = DEFAULT_BUFSIZE;
 
 	return ret;
 }
@@ -946,11 +805,12 @@
 static int uio_open(struct net_device *dev)
 {
 	struct stmmac_priv *priv = netdev_priv(dev);
+	int bfsize = 0;
 	int ret;
 
-	if (priv->hw->pcs != STMMAC_PCS_RGMII &&
-	    priv->hw->pcs != STMMAC_PCS_TBI &&
-	    priv->hw->pcs != STMMAC_PCS_RTBI) {
+	if (priv->hw->pcs != STMMAC_PCS_TBI &&
+	    priv->hw->pcs != STMMAC_PCS_RTBI &&
+	    !priv->hw->xpcs) {
 		ret = rockchip_gmac_uio_init_phy(dev);
 		if (ret) {
 			netdev_err(priv->dev,
@@ -961,11 +821,24 @@
 	}
 
 	/* Extra statistics */
-	memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
 	priv->xstats.threshold = tc;
 
-	priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
+	bfsize = stmmac_set_16kib_bfsize(priv, dev->mtu);
+	if (bfsize < 0)
+		bfsize = 0;
+
+	if (bfsize < BUF_SIZE_16KiB)
+		bfsize = uio_set_bfsize(dev->mtu, priv->dma_buf_sz);
+
+	priv->dma_buf_sz = bfsize;
+	buf_sz = bfsize;
+
 	priv->rx_copybreak = STMMAC_RX_COPYBREAK;
+
+	if (!priv->dma_tx_size)
+		priv->dma_tx_size = DMA_DEFAULT_TX_SIZE;
+	if (!priv->dma_rx_size)
+		priv->dma_rx_size = DMA_DEFAULT_RX_SIZE;
 
 	ret = uio_alloc_dma_desc_resources(priv);
 	if (ret < 0) {
@@ -980,16 +853,16 @@
 		goto init_error;
 	}
 
-	if (dev->phydev)
-		phy_start(dev->phydev);
+	phylink_start(priv->phylink);
+	/* We may have called phylink_speed_down before */
+	phylink_speed_up(priv->phylink);
 
 	return 0;
 
 init_error:
 	uio_free_dma_desc_resources(priv);
 dma_desc_error:
-	if (dev->phydev)
-		phy_disconnect(dev->phydev);
+	phylink_disconnect_phy(priv->phylink);
 	return ret;
 }
 
@@ -1091,12 +964,12 @@
 
 	uio->mem[1].name = "eth_rx_bd";
 	uio->mem[1].addr = priv->rx_queue[0].dma_rx_phy;
-	uio->mem[1].size = DMA_RX_SIZE * sizeof(struct dma_desc);
+	uio->mem[1].size = priv->dma_rx_size * sizeof(struct dma_desc);
 	uio->mem[1].memtype = UIO_MEM_PHYS;
 
 	uio->mem[2].name = "eth_tx_bd";
 	uio->mem[2].addr = priv->tx_queue[0].dma_tx_phy;
-	uio->mem[2].size = DMA_TX_SIZE * sizeof(struct dma_desc);
+	uio->mem[2].size = priv->dma_tx_size * sizeof(struct dma_desc);
 	uio->mem[2].memtype = UIO_MEM_PHYS;
 
 	uio->open = rockchip_gmac_uio_open;
@@ -1148,7 +1021,7 @@
 
 	if (netdev) {
 		rtnl_lock();
-		dev_open(netdev);
+		dev_open(netdev, NULL);
 		rtnl_unlock();
 	}
 
@@ -1175,4 +1048,3 @@
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("ROCKCHIP");
 MODULE_DESCRIPTION("ROCKCHIP GMAC UIO Driver");
-

--
Gitblit v1.6.2