usb: dwc3: qcom: Refactor IRQ handling in glue driver
authorKrishna Kurapati <quic_kriskura@quicinc.com>
Sat, 20 Apr 2024 04:48:59 +0000 (10:18 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Apr 2024 02:56:09 +0000 (19:56 -0700)
On multiport supported controllers, each port has its own DP/DM and
SuperSpeed (if super speed capable) interrupts. As per the bindings,
their interrupt names differ from single-port ones by having a "_x"
added as suffix (x being the port number). Identify from the interrupt
names whether the controller is a multiport controller or not.
Refactor dwc3_qcom_setup_irq() call to parse multiportinterrupts along
with non-multiport ones accordingly.

Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Reviewed-by: Johan Hovold <johan+linaro@kernel.org>
Tested-by: Johan Hovold <johan+linaro@kernel.org>
Link: https://lore.kernel.org/r/20240420044901.884098-8-quic_kriskura@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/dwc3-qcom.c

index cae5dab8fcfc81a7c2fa8b5e989890131ed3c842..5ddb694dd8e72cb40a05c775f72aba1f23ab7d40 100644 (file)
 #define APPS_USB_AVG_BW 0
 #define APPS_USB_PEAK_BW MBps_to_icc(40)
 
+/* Qualcomm SoCs with multiport support has up to 4 ports */
+#define DWC3_QCOM_MAX_PORTS    4
+
+struct dwc3_qcom_port {
+       int                     qusb2_phy_irq;
+       int                     dp_hs_phy_irq;
+       int                     dm_hs_phy_irq;
+       int                     ss_phy_irq;
+};
+
 struct dwc3_qcom {
        struct device           *dev;
        void __iomem            *qscratch_base;
@@ -59,11 +69,8 @@ struct dwc3_qcom {
        struct clk              **clks;
        int                     num_clocks;
        struct reset_control    *resets;
-
-       int                     qusb2_phy_irq;
-       int                     dp_hs_phy_irq;
-       int                     dm_hs_phy_irq;
-       int                     ss_phy_irq;
+       struct dwc3_qcom_port   ports[DWC3_QCOM_MAX_PORTS];
+       u8                      num_ports;
        enum usb_device_speed   usb2_speed;
 
        struct extcon_dev       *edev;
@@ -354,24 +361,24 @@ static void dwc3_qcom_disable_wakeup_irq(int irq)
 
 static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
 {
-       dwc3_qcom_disable_wakeup_irq(qcom->qusb2_phy_irq);
+       dwc3_qcom_disable_wakeup_irq(qcom->ports[0].qusb2_phy_irq);
 
        if (qcom->usb2_speed == USB_SPEED_LOW) {
-               dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
+               dwc3_qcom_disable_wakeup_irq(qcom->ports[0].dm_hs_phy_irq);
        } else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
                        (qcom->usb2_speed == USB_SPEED_FULL)) {
-               dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
+               dwc3_qcom_disable_wakeup_irq(qcom->ports[0].dp_hs_phy_irq);
        } else {
-               dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
-               dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
+               dwc3_qcom_disable_wakeup_irq(qcom->ports[0].dp_hs_phy_irq);
+               dwc3_qcom_disable_wakeup_irq(qcom->ports[0].dm_hs_phy_irq);
        }
 
-       dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq);
+       dwc3_qcom_disable_wakeup_irq(qcom->ports[0].ss_phy_irq);
 }
 
 static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
 {
-       dwc3_qcom_enable_wakeup_irq(qcom->qusb2_phy_irq, 0);
+       dwc3_qcom_enable_wakeup_irq(qcom->ports[0].qusb2_phy_irq, 0);
 
        /*
         * Configure DP/DM line interrupts based on the USB2 device attached to
@@ -383,20 +390,20 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
         */
 
        if (qcom->usb2_speed == USB_SPEED_LOW) {
-               dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
-                                               IRQ_TYPE_EDGE_FALLING);
+               dwc3_qcom_enable_wakeup_irq(qcom->ports[0].dm_hs_phy_irq,
+                                           IRQ_TYPE_EDGE_FALLING);
        } else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
                        (qcom->usb2_speed == USB_SPEED_FULL)) {
-               dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
-                                               IRQ_TYPE_EDGE_FALLING);
+               dwc3_qcom_enable_wakeup_irq(qcom->ports[0].dp_hs_phy_irq,
+                                           IRQ_TYPE_EDGE_FALLING);
        } else {
-               dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
-                                               IRQ_TYPE_EDGE_RISING);
-               dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
-                                               IRQ_TYPE_EDGE_RISING);
+               dwc3_qcom_enable_wakeup_irq(qcom->ports[0].dp_hs_phy_irq,
+                                           IRQ_TYPE_EDGE_RISING);
+               dwc3_qcom_enable_wakeup_irq(qcom->ports[0].dm_hs_phy_irq,
+                                           IRQ_TYPE_EDGE_RISING);
        }
 
-       dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq, 0);
+       dwc3_qcom_enable_wakeup_irq(qcom->ports[0].ss_phy_irq, 0);
 }
 
 static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
@@ -517,42 +524,107 @@ static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
        return ret;
 }
 
-static int dwc3_qcom_setup_irq(struct platform_device *pdev)
+static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
 {
        struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+       const char *irq_name;
        int irq;
        int ret;
 
-       irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
+       if (is_multiport)
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_%d", port_index + 1);
+       else
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_irq");
+       if (!irq_name)
+               return -ENOMEM;
+
+       irq = platform_get_irq_byname_optional(pdev, irq_name);
        if (irq > 0) {
-               ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy");
+               ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
                if (ret)
                        return ret;
-               qcom->qusb2_phy_irq = irq;
+               qcom->ports[port_index].dp_hs_phy_irq = irq;
        }
 
-       irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_irq");
+       if (is_multiport)
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_%d", port_index + 1);
+       else
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_irq");
+       if (!irq_name)
+               return -ENOMEM;
+
+       irq = platform_get_irq_byname_optional(pdev, irq_name);
        if (irq > 0) {
-               ret = dwc3_qcom_request_irq(qcom, irq, "dp_hs_phy_irq");
+               ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
                if (ret)
                        return ret;
-               qcom->dp_hs_phy_irq = irq;
+               qcom->ports[port_index].dm_hs_phy_irq = irq;
        }
 
-       irq = platform_get_irq_byname_optional(pdev, "dm_hs_phy_irq");
+       if (is_multiport)
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_%d", port_index + 1);
+       else
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_irq");
+       if (!irq_name)
+               return -ENOMEM;
+
+       irq = platform_get_irq_byname_optional(pdev, irq_name);
        if (irq > 0) {
-               ret = dwc3_qcom_request_irq(qcom, irq, "dm_hs_phy_irq");
+               ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
                if (ret)
                        return ret;
-               qcom->dm_hs_phy_irq = irq;
+               qcom->ports[port_index].ss_phy_irq = irq;
        }
 
-       irq = platform_get_irq_byname_optional(pdev, "ss_phy_irq");
+       if (is_multiport)
+               return 0;
+
+       irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
        if (irq > 0) {
-               ret = dwc3_qcom_request_irq(qcom, irq, "ss_phy_irq");
+               ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy");
+               if (ret)
+                       return ret;
+               qcom->ports[port_index].qusb2_phy_irq = irq;
+       }
+
+       return 0;
+}
+
+static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
+{
+       char irq_name[14];
+       int port_num;
+       int irq;
+
+       irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
+       if (irq <= 0)
+               return 1;
+
+       for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
+               sprintf(irq_name, "dp_hs_phy_%d", port_num);
+
+               irq = platform_get_irq_byname_optional(pdev, irq_name);
+               if (irq <= 0)
+                       return port_num - 1;
+       }
+
+       return DWC3_QCOM_MAX_PORTS;
+}
+
+static int dwc3_qcom_setup_irq(struct platform_device *pdev)
+{
+       struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+       bool is_multiport;
+       int ret;
+       int i;
+
+       qcom->num_ports = dwc3_qcom_find_num_ports(pdev);
+       is_multiport = (qcom->num_ports > 1);
+
+       for (i = 0; i < qcom->num_ports; i++) {
+               ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
                if (ret)
                        return ret;
-               qcom->ss_phy_irq = irq;
        }
 
        return 0;