mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	AR2315 Ethernet driver pass NULL instead of a real device pointer to DMA (un-)map calls. With kernel version 5.4 such behaviour causes a kernel panic. Fix this issue by preserving device pointer during the probe procedure and pass it to each skb data DMA (un-)map call. Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
		
			
				
	
	
		
			112 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
--- a/drivers/net/ethernet/atheros/ar231x/ar231x.c
 | 
						|
+++ b/drivers/net/ethernet/atheros/ar231x/ar231x.c
 | 
						|
@@ -135,6 +135,7 @@ static int ar231x_mdiobus_write(struct m
 | 
						|
 static int ar231x_mdiobus_reset(struct mii_bus *bus);
 | 
						|
 static int ar231x_mdiobus_probe(struct net_device *dev);
 | 
						|
 static void ar231x_adjust_link(struct net_device *dev);
 | 
						|
+static bool no_phy;
 | 
						|
 
 | 
						|
 #ifndef ERR
 | 
						|
 #define ERR(fmt, args...) printk("%s: " fmt, __func__, ##args)
 | 
						|
@@ -167,6 +168,32 @@ static const struct net_device_ops ar231
 | 
						|
 #endif
 | 
						|
 };
 | 
						|
 
 | 
						|
+static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id)
 | 
						|
+{
 | 
						|
+	int phy_reg;
 | 
						|
+
 | 
						|
+	/**
 | 
						|
+	 * Grab the bits from PHYIR1, and put them
 | 
						|
+	 * in the upper half.
 | 
						|
+	 */
 | 
						|
+	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
 | 
						|
+
 | 
						|
+	if (phy_reg < 0)
 | 
						|
+		return -EIO;
 | 
						|
+
 | 
						|
+	*phy_id = (phy_reg & 0xffff) << 16;
 | 
						|
+
 | 
						|
+	/* Grab the bits from PHYIR2, and put them in the lower half */
 | 
						|
+	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
 | 
						|
+
 | 
						|
+	if (phy_reg < 0)
 | 
						|
+		return -EIO;
 | 
						|
+
 | 
						|
+	*phy_id |= (phy_reg & 0xffff);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int ar231x_probe(struct platform_device *pdev)
 | 
						|
 {
 | 
						|
 	struct net_device *dev;
 | 
						|
@@ -274,6 +301,24 @@ static int ar231x_probe(struct platform_
 | 
						|
 
 | 
						|
 	mdiobus_register(sp->mii_bus);
 | 
						|
 
 | 
						|
+	/**
 | 
						|
+	 * Workaround for Micrel switch, which is only available on
 | 
						|
+	 * one PHY and cannot be configured through MDIO.
 | 
						|
+	 */
 | 
						|
+	if (!no_phy) {
 | 
						|
+		u32 phy_id = 0;
 | 
						|
+
 | 
						|
+		get_phy_id(sp->mii_bus, 1, &phy_id);
 | 
						|
+		if (phy_id == 0x00221450)
 | 
						|
+			no_phy = true;
 | 
						|
+	}
 | 
						|
+	if (no_phy) {
 | 
						|
+		sp->link = 1;
 | 
						|
+		netif_carrier_on(dev);
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+	no_phy = true;
 | 
						|
+
 | 
						|
 	if (ar231x_mdiobus_probe(dev) != 0) {
 | 
						|
 		printk(KERN_ERR "%s: mdiobus_probe failed\n", dev->name);
 | 
						|
 		rx_tasklet_cleanup(dev);
 | 
						|
@@ -327,8 +372,10 @@ static int ar231x_remove(struct platform
 | 
						|
 	rx_tasklet_cleanup(dev);
 | 
						|
 	ar231x_init_cleanup(dev);
 | 
						|
 	unregister_netdev(dev);
 | 
						|
-	mdiobus_unregister(sp->mii_bus);
 | 
						|
-	mdiobus_free(sp->mii_bus);
 | 
						|
+	if (sp->mii_bus) {
 | 
						|
+		mdiobus_unregister(sp->mii_bus);
 | 
						|
+		mdiobus_free(sp->mii_bus);
 | 
						|
+	}
 | 
						|
 	kfree(dev);
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
@@ -871,7 +918,8 @@ static int ar231x_open(struct net_device
 | 
						|
 
 | 
						|
 	sp->eth_regs->mac_control |= MAC_CONTROL_RE;
 | 
						|
 
 | 
						|
-	phy_start(sp->phy_dev);
 | 
						|
+	if (sp->phy_dev)
 | 
						|
+		phy_start(sp->phy_dev);
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
@@ -952,7 +1000,8 @@ static int ar231x_close(struct net_devic
 | 
						|
 
 | 
						|
 #endif
 | 
						|
 
 | 
						|
-	phy_stop(sp->phy_dev);
 | 
						|
+	if (sp->phy_dev)
 | 
						|
+		phy_stop(sp->phy_dev);
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
@@ -996,6 +1045,9 @@ static int ar231x_ioctl(struct net_devic
 | 
						|
 {
 | 
						|
 	struct ar231x_private *sp = netdev_priv(dev);
 | 
						|
 
 | 
						|
+	if (!sp->phy_dev)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
 	switch (cmd) {
 | 
						|
 	case SIOCGMIIPHY:
 | 
						|
 	case SIOCGMIIREG:
 |