Merge tag 'v5.15.64' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / net / usb / smsc95xx.c
index cedd187..7b0d7dc 100644 (file)
@@ -18,8 +18,6 @@
 #include <linux/usb/usbnet.h>
 #include <linux/slab.h>
 #include <linux/of_net.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
 #include <linux/mdio.h>
 #include <linux/phy.h>
 #include "smsc95xx.h"
@@ -54,9 +52,6 @@
                                         SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
 #define MAC_ADDR_LEN                    (6)
 
-#define SMSC95XX_NR_IRQS               (1) /* raise to 12 for GPIOs */
-#define PHY_HWIRQ                      (SMSC95XX_NR_IRQS - 1)
-
 struct smsc95xx_priv {
        u32 mac_cr;
        u32 hash_hi;
@@ -65,12 +60,8 @@ struct smsc95xx_priv {
        spinlock_t mac_cr_lock;
        u8 features;
        u8 suspend_flags;
-       struct irq_chip irqchip;
-       struct irq_domain *irqdomain;
-       struct fwnode_handle *irqfwnode;
        struct mii_bus *mdiobus;
        struct phy_device *phydev;
-       struct task_struct *pm_task;
 };
 
 static bool turbo_mode = true;
@@ -92,14 +83,13 @@ MODULE_PARM_DESC(macaddr, "MAC address");
 static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
                                            u32 *data, int in_pm)
 {
-       struct smsc95xx_priv *pdata = dev->driver_priv;
        u32 buf;
        int ret;
        int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
 
        BUG_ON(!dev);
 
-       if (current != pdata->pm_task)
+       if (!in_pm)
                fn = usbnet_read_cmd;
        else
                fn = usbnet_read_cmd_nopm;
@@ -123,14 +113,13 @@ static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
 static int __must_check __smsc95xx_write_reg(struct usbnet *dev, u32 index,
                                             u32 data, int in_pm)
 {
-       struct smsc95xx_priv *pdata = dev->driver_priv;
        u32 buf;
        int ret;
        int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
 
        BUG_ON(!dev);
 
-       if (current != pdata->pm_task)
+       if (!in_pm)
                fn = usbnet_write_cmd;
        else
                fn = usbnet_write_cmd_nopm;
@@ -619,8 +608,6 @@ static void smsc95xx_mac_update_fullduplex(struct usbnet *dev)
 
 static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
 {
-       struct smsc95xx_priv *pdata = dev->driver_priv;
-       unsigned long flags;
        u32 intdata;
 
        if (urb->actual_length != 4) {
@@ -632,15 +619,11 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
        intdata = get_unaligned_le32(urb->transfer_buffer);
        netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
 
-       local_irq_save(flags);
-
        if (intdata & INT_ENP_PHY_INT_)
-               generic_handle_domain_irq(pdata->irqdomain, PHY_HWIRQ);
+               ;
        else
                netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n",
                            intdata);
-
-       local_irq_restore(flags);
 }
 
 /* Enable or disable Tx & Rx checksum offload engines */
@@ -1153,9 +1136,8 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        struct smsc95xx_priv *pdata;
        bool is_internal_phy;
-       char usb_path[64];
-       int ret, phy_irq;
        u32 val;
+       int ret;
 
        printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
 
@@ -1195,38 +1177,10 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
        if (ret)
                goto free_pdata;
 
-       /* create irq domain for use by PHY driver and GPIO consumers */
-       usb_make_path(dev->udev, usb_path, sizeof(usb_path));
-       pdata->irqfwnode = irq_domain_alloc_named_fwnode(usb_path);
-       if (!pdata->irqfwnode) {
-               ret = -ENOMEM;
-               goto free_pdata;
-       }
-
-       pdata->irqdomain = irq_domain_create_linear(pdata->irqfwnode,
-                                                   SMSC95XX_NR_IRQS,
-                                                   &irq_domain_simple_ops,
-                                                   pdata);
-       if (!pdata->irqdomain) {
-               ret = -ENOMEM;
-               goto free_irqfwnode;
-       }
-
-       phy_irq = irq_create_mapping(pdata->irqdomain, PHY_HWIRQ);
-       if (!phy_irq) {
-               ret = -ENOENT;
-               goto remove_irqdomain;
-       }
-
-       pdata->irqchip = dummy_irq_chip;
-       pdata->irqchip.name = SMSC_CHIPNAME;
-       irq_set_chip_and_handler_name(phy_irq, &pdata->irqchip,
-                                     handle_simple_irq, "phy");
-
        pdata->mdiobus = mdiobus_alloc();
        if (!pdata->mdiobus) {
                ret = -ENOMEM;
-               goto dispose_irq;
+               goto free_pdata;
        }
 
        ret = smsc95xx_read_reg(dev, HW_CFG, &val);
@@ -1259,7 +1213,6 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
                goto unregister_mdio;
        }
 
-       pdata->phydev->irq = phy_irq;
        pdata->phydev->is_internal = is_internal_phy;
 
        /* detect device revision as different features may be available */
@@ -1302,15 +1255,6 @@ unregister_mdio:
 free_mdio:
        mdiobus_free(pdata->mdiobus);
 
-dispose_irq:
-       irq_dispose_mapping(phy_irq);
-
-remove_irqdomain:
-       irq_domain_remove(pdata->irqdomain);
-
-free_irqfwnode:
-       irq_domain_free_fwnode(pdata->irqfwnode);
-
 free_pdata:
        kfree(pdata);
        return ret;
@@ -1323,9 +1267,6 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
        phy_disconnect(dev->net->phydev);
        mdiobus_unregister(pdata->mdiobus);
        mdiobus_free(pdata->mdiobus);
-       irq_dispose_mapping(irq_find_mapping(pdata->irqdomain, PHY_HWIRQ));
-       irq_domain_remove(pdata->irqdomain);
-       irq_domain_free_fwnode(pdata->irqfwnode);
        netif_dbg(dev, ifdown, dev->net, "free pdata\n");
        kfree(pdata);
 }
@@ -1350,6 +1291,29 @@ static u32 smsc_crc(const u8 *buffer, size_t len, int filter)
        return crc << ((filter % 2) * 16);
 }
 
+static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask)
+{
+       int ret;
+
+       netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n");
+
+       /* read to clear */
+       ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_SRC);
+       if (ret < 0)
+               return ret;
+
+       /* enable interrupt source */
+       ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_MASK);
+       if (ret < 0)
+               return ret;
+
+       ret |= mask;
+
+       smsc95xx_mdio_write_nopm(dev, PHY_INT_MASK, ret);
+
+       return 0;
+}
+
 static int smsc95xx_link_ok_nopm(struct usbnet *dev)
 {
        int ret;
@@ -1516,6 +1480,7 @@ static int smsc95xx_enter_suspend3(struct usbnet *dev)
 static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
 {
        struct smsc95xx_priv *pdata = dev->driver_priv;
+       int ret;
 
        if (!netif_running(dev->net)) {
                /* interface is ifconfig down so fully power down hw */
@@ -1534,10 +1499,27 @@ static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
                }
 
                netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
+
+               /* enable PHY wakeup events for if cable is attached */
+               ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+                       PHY_INT_MASK_ANEG_COMP_);
+               if (ret < 0) {
+                       netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
+                       return ret;
+               }
+
                netdev_info(dev->net, "entering SUSPEND1 mode\n");
                return smsc95xx_enter_suspend1(dev);
        }
 
+       /* enable PHY wakeup events so we remote wakeup if cable is pulled */
+       ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+               PHY_INT_MASK_LINK_DOWN_);
+       if (ret < 0) {
+               netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
+               return ret;
+       }
+
        netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n");
        return smsc95xx_enter_suspend3(dev);
 }
@@ -1549,12 +1531,9 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
        u32 val, link_up;
        int ret;
 
-       pdata->pm_task = current;
-
        ret = usbnet_suspend(intf, message);
        if (ret < 0) {
                netdev_warn(dev->net, "usbnet_suspend error\n");
-               pdata->pm_task = NULL;
                return ret;
        }
 
@@ -1606,6 +1585,13 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
        }
 
        if (pdata->wolopts & WAKE_PHY) {
+               ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+                       (PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_));
+               if (ret < 0) {
+                       netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
+                       goto done;
+               }
+
                /* if link is down then configure EDPD and enter SUSPEND1,
                 * otherwise enter SUSPEND0 below
                 */
@@ -1794,7 +1780,6 @@ done:
        if (ret && PMSG_IS_AUTO(message))
                usbnet_resume(intf);
 
-       pdata->pm_task = NULL;
        return ret;
 }
 
@@ -1815,53 +1800,45 @@ static int smsc95xx_resume(struct usb_interface *intf)
        /* do this first to ensure it's cleared even in error case */
        pdata->suspend_flags = 0;
 
-       pdata->pm_task = current;
-
        if (suspend_flags & SUSPEND_ALLMODES) {
                /* clear wake-up sources */
                ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
                if (ret < 0)
-                       goto done;
+                       return ret;
 
                val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_);
 
                ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
                if (ret < 0)
-                       goto done;
+                       return ret;
 
                /* clear wake-up status */
                ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
                if (ret < 0)
-                       goto done;
+                       return ret;
 
                val &= ~PM_CTL_WOL_EN_;
                val |= PM_CTL_WUPS_;
 
                ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
                if (ret < 0)
-                       goto done;
+                       return ret;
        }
 
-       phy_init_hw(pdata->phydev);
-
        ret = usbnet_resume(intf);
        if (ret < 0)
                netdev_warn(dev->net, "usbnet_resume error\n");
 
-done:
-       pdata->pm_task = NULL;
+       phy_init_hw(pdata->phydev);
        return ret;
 }
 
 static int smsc95xx_reset_resume(struct usb_interface *intf)
 {
        struct usbnet *dev = usb_get_intfdata(intf);
-       struct smsc95xx_priv *pdata = dev->driver_priv;
        int ret;
 
-       pdata->pm_task = current;
        ret = smsc95xx_reset(dev);
-       pdata->pm_task = NULL;
        if (ret < 0)
                return ret;