Commit 2bfc9916 authored by Krishna Kurapati's avatar Krishna Kurapati Committed by Greg Kroah-Hartman
Browse files

usb: dwc3: qcom: Refactor IRQ handling in glue driver



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: default avatarKrishna Kurapati <quic_kriskura@quicinc.com>
Reviewed-by: default avatarBjorn Andersson <quic_bjorande@quicinc.com>
Acked-by: default avatarThinh Nguyen <Thinh.Nguyen@synopsys.com>
Reviewed-by: default avatarJohan Hovold <johan+linaro@kernel.org>
Tested-by: default avatarJohan Hovold <johan+linaro@kernel.org>
Link: https://lore.kernel.org/r/20240420044901.884098-8-quic_kriskura@quicinc.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6410c803
Loading
Loading
Loading
Loading
+106 −34
Original line number Diff line number Diff line
@@ -52,6 +52,16 @@
#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,
		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,
		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,
		dwc3_qcom_enable_wakeup_irq(qcom->ports[0].dp_hs_phy_irq,
					    IRQ_TYPE_EDGE_RISING);
		dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
		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;