Message ID | 20240206051825.1038685-1-quic_kriskura@quicinc.com |
---|---|
Headers | show |
Series | Add multiport support for DWC3 controllers | expand |
On Tue, Feb 06, 2024 at 10:48:19AM +0530, Krishna Kurapati wrote: > On some SoC's like SA8295P where the tertiary controller is host-only > capable, GEVTADDRHI/LO, GEVTSIZ, GEVTCOUNT registers are not accessible. > Trying to access them leads to a crash. > > For DRD/Peripheral supported controllers, event buffer setup is done > again in gadget_pullup. Skip setup or cleanup of event buffers if > controller is host-only capable. > > Suggested-by: Johan Hovold <johan@kernel.org> > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> > Reviewed-by: Johan Hovold <johan+linaro@kernel.org> Reviewed-by: Bjorn Andersson <andersson@kernel.org> Regards, Bjorn > --- > drivers/usb/dwc3/core.c | 13 +++++++++++++ > 1 file changed, 13 insertions(+) > > diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c > index 965eaad195fb..c47fec10b231 100644 > --- a/drivers/usb/dwc3/core.c > +++ b/drivers/usb/dwc3/core.c > @@ -486,6 +486,13 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) > static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length) > { > struct dwc3_event_buffer *evt; > + unsigned int hw_mode; > + > + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); > + if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) { > + dwc->ev_buf = NULL; > + return 0; > + } > > evt = dwc3_alloc_one_event_buffer(dwc, length); > if (IS_ERR(evt)) { > @@ -507,6 +514,9 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc) > { > struct dwc3_event_buffer *evt; > > + if (!dwc->ev_buf) > + return 0; > + > evt = dwc->ev_buf; > evt->lpos = 0; > dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), > @@ -524,6 +534,9 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc) > { > struct dwc3_event_buffer *evt; > > + if (!dwc->ev_buf) > + return; > + > evt = dwc->ev_buf; > > evt->lpos = 0; > -- > 2.34.1 >
On Tue, Feb 06, 2024 at 10:48:21AM +0530, Krishna Kurapati wrote: > Add the compatible string for SC8280 Multiport USB controller from > Qualcomm. > > There are 4 power event irq interrupts supported by this controller > (one for each port of multiport). Added all the 4 as non-optional > interrupts for SC8280XP-MP > > Also each port of multiport has one DP and oen DM IRQ. Add all DP/DM > IRQ's related to 4 ports of SC8280XP Teritiary controller. > > Also added ss phy irq for both SS Ports. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> Reviewed-by: Bjorn Andersson <andersson@kernel.org> Regards, Bjorn > --- > .../devicetree/bindings/usb/qcom,dwc3.yaml | 33 +++++++++++++++++++ > 1 file changed, 33 insertions(+) > > diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml > index 63d150b216c5..cc7cf592c029 100644 > --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml > +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml > @@ -30,6 +30,7 @@ properties: > - qcom,sc7180-dwc3 > - qcom,sc7280-dwc3 > - qcom,sc8280xp-dwc3 > + - qcom,sc8280xp-dwc3-mp > - qcom,sdm660-dwc3 > - qcom,sdm670-dwc3 > - qcom,sdm845-dwc3 > @@ -282,6 +283,7 @@ allOf: > contains: > enum: > - qcom,sc8280xp-dwc3 > + - qcom,sc8280xp-dwc3-mp > - qcom,x1e80100-dwc3 > then: > properties: > @@ -470,6 +472,37 @@ allOf: > - const: dm_hs_phy_irq > - const: ss_phy_irq > > + - if: > + properties: > + compatible: > + contains: > + enum: > + - qcom,sc8280xp-dwc3-mp > + then: > + properties: > + interrupts: > + maxItems: 18 > + interrupt-names: > + items: > + - const: pwr_event_1 > + - const: pwr_event_2 > + - const: pwr_event_3 > + - const: pwr_event_4 > + - const: hs_phy_1 > + - const: hs_phy_2 > + - const: hs_phy_3 > + - const: hs_phy_4 > + - const: dp_hs_phy_1 > + - const: dm_hs_phy_1 > + - const: dp_hs_phy_2 > + - const: dm_hs_phy_2 > + - const: dp_hs_phy_3 > + - const: dm_hs_phy_3 > + - const: dp_hs_phy_4 > + - const: dm_hs_phy_4 > + - const: ss_phy_1 > + - const: ss_phy_2 > + > additionalProperties: false > > examples: > -- > 2.34.1 >
On Tue, Feb 06, 2024 at 10:48:23AM +0530, Krishna Kurapati wrote: > On multiport supported controllers, each port has its own DP/DM > and SS (if super speed capable) interrupts. As per the bindings, > their interrupt names differ from standard ones having "_x" added > as suffix (x indicates port number). Refactor dwc3_qcom_setup_irq() > call to parse multiport interrupts along with non-multiport ones. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> Reviewed-by: Bjorn Andersson <andersson@kernel.org> Regards, Bjorn > --- > drivers/usb/dwc3/dwc3-qcom.c | 222 +++++++++++++++++++++++++---------- > 1 file changed, 161 insertions(+), 61 deletions(-) > > diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c > index 08df29584366..a20d63a791bd 100644 > --- a/drivers/usb/dwc3/dwc3-qcom.c > +++ b/drivers/usb/dwc3/dwc3-qcom.c > @@ -53,17 +53,33 @@ > #define APPS_USB_AVG_BW 0 > #define APPS_USB_PEAK_BW MBps_to_icc(40) > > +#define NUM_PHY_IRQ 4 > + > +enum dwc3_qcom_phy_index { > + DP_HS_PHY_IRQ_INDEX, > + DM_HS_PHY_IRQ_INDEX, > + SS_PHY_IRQ_INDEX, > + QUSB2_PHY_IRQ_INDEX, > +}; > + > struct dwc3_acpi_pdata { > u32 qscratch_base_offset; > u32 qscratch_base_size; > u32 dwc3_core_base_size; > - int qusb2_phy_irq_index; > - int dp_hs_phy_irq_index; > - int dm_hs_phy_irq_index; > - int ss_phy_irq_index; > + /* > + * The phy_irq_index corresponds to ACPI indexes of (in order) > + * DP/DM/SS/QUSB2 IRQ's respectively. > + */ > + int phy_irq_index[NUM_PHY_IRQ]; > bool is_urs; > }; > > +struct dwc3_qcom_port { > + int dp_hs_phy_irq; > + int dm_hs_phy_irq; > + int ss_phy_irq; > +}; > + > struct dwc3_qcom { > struct device *dev; > void __iomem *qscratch_base; > @@ -74,9 +90,7 @@ struct dwc3_qcom { > 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 port_info[DWC3_MAX_PORTS]; > enum usb_device_speed usb2_speed; > > struct extcon_dev *edev; > @@ -91,6 +105,7 @@ struct dwc3_qcom { > bool pm_suspended; > struct icc_path *icc_path_ddr; > struct icc_path *icc_path_apps; > + u8 num_ports; > }; > > static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) > @@ -375,16 +390,16 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) > dwc3_qcom_disable_wakeup_irq(qcom->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->port_info[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->port_info[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->port_info[0].dp_hs_phy_irq); > + dwc3_qcom_disable_wakeup_irq(qcom->port_info[0].dm_hs_phy_irq); > } > > - dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq); > + dwc3_qcom_disable_wakeup_irq(qcom->port_info[0].ss_phy_irq); > } > > static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) > @@ -401,20 +416,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->port_info[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->port_info[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->port_info[0].dp_hs_phy_irq, > + IRQ_TYPE_EDGE_RISING); > + dwc3_qcom_enable_wakeup_irq(qcom->port_info[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->port_info[0].ss_phy_irq, 0); > } > > static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) > @@ -535,6 +550,74 @@ static int dwc3_qcom_get_irq(struct platform_device *pdev, > return ret; > } > > +static int dwc3_qcom_get_irq_index(const char *irq_name) > +{ > + /* > + * Parse IRQ index based on prefixes from interrupt name. > + * Return -1 incase of an invalid interrupt name. > + */ > + int irq_index = -1; > + > + if (strncmp(irq_name, "dp_hs_phy", strlen("dp_hs_phy")) == 0) > + irq_index = DP_HS_PHY_IRQ_INDEX; > + else if (strncmp(irq_name, "dm_hs_phy", strlen("dm_hs_phy")) == 0) > + irq_index = DM_HS_PHY_IRQ_INDEX; > + else if (strncmp(irq_name, "ss_phy", strlen("ss_phy")) == 0) > + irq_index = SS_PHY_IRQ_INDEX; > + else if (strncmp(irq_name, "qusb2_phy", strlen("qusb2_phy")) == 0) > + irq_index = QUSB2_PHY_IRQ_INDEX; > + return irq_index; > +} > + > +static int dwc3_qcom_get_port_index(const char *irq_name, int irq_index) > +{ > + int port_index = -1; > + > + switch (irq_index) { > + case DP_HS_PHY_IRQ_INDEX: > + if (strcmp(irq_name, "dp_hs_phy_irq") == 0) > + port_index = 1; > + else > + sscanf(irq_name, "dp_hs_phy_%d", &port_index); > + break; > + case DM_HS_PHY_IRQ_INDEX: > + if (strcmp(irq_name, "dm_hs_phy_irq") == 0) > + port_index = 1; > + else > + sscanf(irq_name, "dm_hs_phy_%d", &port_index); > + break; > + case SS_PHY_IRQ_INDEX: > + if (strcmp(irq_name, "ss_phy_irq") == 0) > + port_index = 1; > + else > + sscanf(irq_name, "ss_phy_%d", &port_index); > + break; > + case QUSB2_PHY_IRQ_INDEX: > + port_index = 1; > + break; > + } > + > + if (port_index <= 0 || port_index > DWC3_MAX_PORTS) > + port_index = -1; > + > + return port_index; > +} > + > +static int dwc3_qcom_get_acpi_index(struct dwc3_qcom *qcom, int irq_index, > + int port_index) > +{ > + const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; > + > + /* > + * Currently multiport supported targets don't have an ACPI variant. > + * So return -1 if we are not dealing with first port of the controller. > + */ > + if (!pdata || port_index != 1) > + return -1; > + > + return pdata->phy_irq_index[irq_index]; > +} > + > static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq, > const char *name) > { > @@ -554,44 +637,67 @@ static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq, > static int dwc3_qcom_setup_irq(struct platform_device *pdev) > { > struct dwc3_qcom *qcom = platform_get_drvdata(pdev); > - const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; > + struct device_node *np = pdev->dev.of_node; > + const char **irq_names; > + int port_index; > + int acpi_index; > + int irq_count; > + int irq_index; > int irq; > int ret; > + int i; > > - irq = dwc3_qcom_get_irq(pdev, "qusb2_phy", > - pdata ? pdata->qusb2_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "hs_phy_irq"); > - if (ret) > - return ret; > - qcom->qusb2_phy_irq = irq; > - } > + irq_count = of_property_count_strings(np, "interrupt-names"); > + if (irq_count < 0) > + return -EINVAL; > > - irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", > - pdata ? pdata->dp_hs_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "dp_hs_phy_irq"); > - if (ret) > - return ret; > - qcom->dp_hs_phy_irq = irq; > - } > + irq_names = devm_kcalloc(&pdev->dev, irq_count, sizeof(*irq_names), GFP_KERNEL); > + if (!irq_names) > + return -ENOMEM; > > - irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq", > - pdata ? pdata->dm_hs_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "dm_hs_phy_irq"); > - if (ret) > - return ret; > - qcom->dm_hs_phy_irq = irq; > - } > + ret = of_property_read_string_array(np, "interrupt-names", > + irq_names, irq_count); > + if (!ret) > + return ret; > > - irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq", > - pdata ? pdata->ss_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "ss_phy_irq"); > - if (ret) > - return ret; > - qcom->ss_phy_irq = irq; > + for (i = 0; i < irq_count; i++) { > + irq_index = dwc3_qcom_get_irq_index(irq_names[i]); > + if (irq_index == -1) { > + dev_err(&pdev->dev, "Unknown interrupt-name \"%s\" found\n", irq_names[i]); > + continue; > + } > + port_index = dwc3_qcom_get_port_index(irq_names[i], irq_index); > + if (port_index == -1) { > + dev_err(&pdev->dev, "Invalid interrupt-name suffix \"%s\"\n", irq_names[i]); > + continue; > + } > + > + acpi_index = dwc3_qcom_get_acpi_index(qcom, irq_index, port_index); > + > + irq = dwc3_qcom_get_irq(pdev, irq_names[i], acpi_index); > + if (irq > 0) { > + ret = dwc3_qcom_request_irq(qcom, irq, irq_names[i]); > + if (ret) > + return ret; > + > + switch (irq_index) { > + case DP_HS_PHY_IRQ_INDEX: > + qcom->port_info[port_index - 1].dp_hs_phy_irq = irq; > + break; > + case DM_HS_PHY_IRQ_INDEX: > + qcom->port_info[port_index - 1].dm_hs_phy_irq = irq; > + break; > + case SS_PHY_IRQ_INDEX: > + qcom->port_info[port_index - 1].ss_phy_irq = irq; > + break; > + case QUSB2_PHY_IRQ_INDEX: > + qcom->qusb2_phy_irq = irq; > + break; > + } > + > + if (qcom->num_ports < port_index) > + qcom->num_ports = port_index; > + } > } > > return 0; > @@ -1053,20 +1159,14 @@ static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { > .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, > .qscratch_base_size = SDM845_QSCRATCH_SIZE, > .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, > - .qusb2_phy_irq_index = 1, > - .dp_hs_phy_irq_index = 4, > - .dm_hs_phy_irq_index = 3, > - .ss_phy_irq_index = 2 > + .phy_irq_index = {4, 3, 2, 1}, > }; > > static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = { > .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, > .qscratch_base_size = SDM845_QSCRATCH_SIZE, > .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, > - .qusb2_phy_irq_index = 1, > - .dp_hs_phy_irq_index = 4, > - .dm_hs_phy_irq_index = 3, > - .ss_phy_irq_index = 2, > + .phy_irq_index = {4, 3, 2, 1}, > .is_urs = true, > }; > > -- > 2.34.1 >
On Tue, Feb 06, 2024 at 10:48:25AM +0530, Krishna Kurapati wrote: > Power event IRQ stat registers are present for each port > connected to controller. Add support for modifying all power event > irq stat registers present in wrapper. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> Reviewed-by: Bjorn Andersson <andersson@kernel.org> Regards, Bjorn > --- > drivers/usb/dwc3/dwc3-qcom.c | 30 +++++++++++++++++++++++------- > 1 file changed, 23 insertions(+), 7 deletions(-) > > diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c > index 572dc3fdae12..e789745a9468 100644 > --- a/drivers/usb/dwc3/dwc3-qcom.c > +++ b/drivers/usb/dwc3/dwc3-qcom.c > @@ -37,7 +37,11 @@ > #define PIPE3_PHYSTATUS_SW BIT(3) > #define PIPE_UTMI_CLK_DIS BIT(8) > > -#define PWR_EVNT_IRQ_STAT_REG 0x58 > +#define PWR_EVNT_IRQ1_STAT_REG 0x58 > +#define PWR_EVNT_IRQ2_STAT_REG 0x1dc > +#define PWR_EVNT_IRQ3_STAT_REG 0x228 > +#define PWR_EVNT_IRQ4_STAT_REG 0x238 > + > #define PWR_EVNT_LPM_IN_L2_MASK BIT(4) > #define PWR_EVNT_LPM_OUT_L2_MASK BIT(5) > > @@ -109,6 +113,13 @@ struct dwc3_qcom { > u8 num_ports; > }; > > +static const u32 pwr_evnt_irq_stat_reg_offset[DWC3_MAX_PORTS] = { > + PWR_EVNT_IRQ1_STAT_REG, > + PWR_EVNT_IRQ2_STAT_REG, > + PWR_EVNT_IRQ3_STAT_REG, > + PWR_EVNT_IRQ4_STAT_REG, > +}; > + > static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) > { > u32 reg; > @@ -444,9 +455,11 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) > if (qcom->is_suspended) > return 0; > > - val = readl(qcom->qscratch_base + PWR_EVNT_IRQ_STAT_REG); > - if (!(val & PWR_EVNT_LPM_IN_L2_MASK)) > - dev_err(qcom->dev, "HS-PHY not in L2\n"); > + for (i = 0; i < qcom->num_ports; i++) { > + val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg_offset[i]); > + if (!(val & PWR_EVNT_LPM_IN_L2_MASK)) > + dev_err(qcom->dev, "Port-%d HS-PHY not in L2\n", i + 1); > + } > > for (i = qcom->num_clocks - 1; i >= 0; i--) > clk_disable_unprepare(qcom->clks[i]); > @@ -491,9 +504,12 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) > if (ret) > dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret); > > - /* Clear existing events from PHY related to L2 in/out */ > - dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG, > - PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); > + for (i = 0; i < qcom->num_ports; i++) { > + /* Clear existing events from PHY related to L2 in/out */ > + dwc3_qcom_setbits(qcom->qscratch_base, > + pwr_evnt_irq_stat_reg_offset[i], > + PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); > + } > > qcom->is_suspended = false; > > -- > 2.34.1 >
On Tue, Feb 06, 2024 at 10:48:17AM +0530, Krishna Kurapati wrote: > Add bindings to indicate properties required to support multiport > on Synopsys DWC3 controller. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> Reviewed-by: Bjorn Andersson <andersson@kernel.org> Regards, Bjorn > --- > .../devicetree/bindings/usb/snps,dwc3.yaml | 13 +++++++------ > 1 file changed, 7 insertions(+), 6 deletions(-) > > diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml > index 8f5d250070c7..9227e200bcab 100644 > --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml > +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml > @@ -85,15 +85,16 @@ properties: > > phys: > minItems: 1 > - maxItems: 2 > + maxItems: 8 > > phy-names: > minItems: 1 > - maxItems: 2 > - items: > - enum: > - - usb2-phy > - - usb3-phy > + maxItems: 8 > + oneOf: > + - items: > + enum: [ usb2-phy, usb3-phy ] > + - items: > + pattern: "^usb[23]-[0-3]$" > > power-domains: > description: > -- > 2.34.1 >
On Tue, 06 Feb 2024 10:48:17 +0530, Krishna Kurapati wrote: > Add bindings to indicate properties required to support multiport > on Synopsys DWC3 controller. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > --- > .../devicetree/bindings/usb/snps,dwc3.yaml | 13 +++++++------ > 1 file changed, 7 insertions(+), 6 deletions(-) > Reviewed-by: Rob Herring <robh@kernel.org>
On Tue, Feb 06, 2024, Krishna Kurapati wrote: > On multiport supported controllers, each port has its own DP/DM > and SS (if super speed capable) interrupts. As per the bindings, > their interrupt names differ from standard ones having "_x" added > as suffix (x indicates port number). Refactor dwc3_qcom_setup_irq() > call to parse multiport interrupts along with non-multiport ones. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > --- > drivers/usb/dwc3/dwc3-qcom.c | 222 +++++++++++++++++++++++++---------- > 1 file changed, 161 insertions(+), 61 deletions(-) > > diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c > index 08df29584366..a20d63a791bd 100644 > --- a/drivers/usb/dwc3/dwc3-qcom.c > +++ b/drivers/usb/dwc3/dwc3-qcom.c > @@ -53,17 +53,33 @@ > #define APPS_USB_AVG_BW 0 > #define APPS_USB_PEAK_BW MBps_to_icc(40) > > +#define NUM_PHY_IRQ 4 > + > +enum dwc3_qcom_phy_index { > + DP_HS_PHY_IRQ_INDEX, > + DM_HS_PHY_IRQ_INDEX, > + SS_PHY_IRQ_INDEX, > + QUSB2_PHY_IRQ_INDEX, > +}; > + > struct dwc3_acpi_pdata { > u32 qscratch_base_offset; > u32 qscratch_base_size; > u32 dwc3_core_base_size; > - int qusb2_phy_irq_index; > - int dp_hs_phy_irq_index; > - int dm_hs_phy_irq_index; > - int ss_phy_irq_index; > + /* > + * The phy_irq_index corresponds to ACPI indexes of (in order) > + * DP/DM/SS/QUSB2 IRQ's respectively. > + */ > + int phy_irq_index[NUM_PHY_IRQ]; > bool is_urs; > }; > > +struct dwc3_qcom_port { > + int dp_hs_phy_irq; > + int dm_hs_phy_irq; > + int ss_phy_irq; > +}; > + > struct dwc3_qcom { > struct device *dev; > void __iomem *qscratch_base; > @@ -74,9 +90,7 @@ struct dwc3_qcom { > 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 port_info[DWC3_MAX_PORTS]; > enum usb_device_speed usb2_speed; > > struct extcon_dev *edev; > @@ -91,6 +105,7 @@ struct dwc3_qcom { > bool pm_suspended; > struct icc_path *icc_path_ddr; > struct icc_path *icc_path_apps; > + u8 num_ports; > }; > > static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) > @@ -375,16 +390,16 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) > dwc3_qcom_disable_wakeup_irq(qcom->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->port_info[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->port_info[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->port_info[0].dp_hs_phy_irq); > + dwc3_qcom_disable_wakeup_irq(qcom->port_info[0].dm_hs_phy_irq); > } > > - dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq); > + dwc3_qcom_disable_wakeup_irq(qcom->port_info[0].ss_phy_irq); > } > > static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) > @@ -401,20 +416,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->port_info[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->port_info[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->port_info[0].dp_hs_phy_irq, > + IRQ_TYPE_EDGE_RISING); > + dwc3_qcom_enable_wakeup_irq(qcom->port_info[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->port_info[0].ss_phy_irq, 0); > } > > static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) > @@ -535,6 +550,74 @@ static int dwc3_qcom_get_irq(struct platform_device *pdev, > return ret; > } > > +static int dwc3_qcom_get_irq_index(const char *irq_name) > +{ > + /* > + * Parse IRQ index based on prefixes from interrupt name. > + * Return -1 incase of an invalid interrupt name. > + */ > + int irq_index = -1; > + > + if (strncmp(irq_name, "dp_hs_phy", strlen("dp_hs_phy")) == 0) > + irq_index = DP_HS_PHY_IRQ_INDEX; > + else if (strncmp(irq_name, "dm_hs_phy", strlen("dm_hs_phy")) == 0) > + irq_index = DM_HS_PHY_IRQ_INDEX; > + else if (strncmp(irq_name, "ss_phy", strlen("ss_phy")) == 0) > + irq_index = SS_PHY_IRQ_INDEX; > + else if (strncmp(irq_name, "qusb2_phy", strlen("qusb2_phy")) == 0) > + irq_index = QUSB2_PHY_IRQ_INDEX; > + return irq_index; > +} > + > +static int dwc3_qcom_get_port_index(const char *irq_name, int irq_index) > +{ > + int port_index = -1; > + > + switch (irq_index) { > + case DP_HS_PHY_IRQ_INDEX: > + if (strcmp(irq_name, "dp_hs_phy_irq") == 0) > + port_index = 1; > + else > + sscanf(irq_name, "dp_hs_phy_%d", &port_index); > + break; > + case DM_HS_PHY_IRQ_INDEX: > + if (strcmp(irq_name, "dm_hs_phy_irq") == 0) > + port_index = 1; > + else > + sscanf(irq_name, "dm_hs_phy_%d", &port_index); > + break; > + case SS_PHY_IRQ_INDEX: > + if (strcmp(irq_name, "ss_phy_irq") == 0) > + port_index = 1; > + else > + sscanf(irq_name, "ss_phy_%d", &port_index); > + break; > + case QUSB2_PHY_IRQ_INDEX: > + port_index = 1; > + break; > + } > + > + if (port_index <= 0 || port_index > DWC3_MAX_PORTS) > + port_index = -1; > + > + return port_index; > +} > + > +static int dwc3_qcom_get_acpi_index(struct dwc3_qcom *qcom, int irq_index, > + int port_index) > +{ > + const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; > + > + /* > + * Currently multiport supported targets don't have an ACPI variant. > + * So return -1 if we are not dealing with first port of the controller. > + */ > + if (!pdata || port_index != 1) > + return -1; > + > + return pdata->phy_irq_index[irq_index]; > +} > + > static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq, > const char *name) > { > @@ -554,44 +637,67 @@ static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq, > static int dwc3_qcom_setup_irq(struct platform_device *pdev) > { > struct dwc3_qcom *qcom = platform_get_drvdata(pdev); > - const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; > + struct device_node *np = pdev->dev.of_node; > + const char **irq_names; > + int port_index; > + int acpi_index; > + int irq_count; > + int irq_index; > int irq; > int ret; > + int i; > > - irq = dwc3_qcom_get_irq(pdev, "qusb2_phy", > - pdata ? pdata->qusb2_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "hs_phy_irq"); > - if (ret) > - return ret; > - qcom->qusb2_phy_irq = irq; > - } > + irq_count = of_property_count_strings(np, "interrupt-names"); > + if (irq_count < 0) > + return -EINVAL; > > - irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", > - pdata ? pdata->dp_hs_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "dp_hs_phy_irq"); > - if (ret) > - return ret; > - qcom->dp_hs_phy_irq = irq; > - } > + irq_names = devm_kcalloc(&pdev->dev, irq_count, sizeof(*irq_names), GFP_KERNEL); > + if (!irq_names) > + return -ENOMEM; > > - irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq", > - pdata ? pdata->dm_hs_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "dm_hs_phy_irq"); > - if (ret) > - return ret; > - qcom->dm_hs_phy_irq = irq; > - } > + ret = of_property_read_string_array(np, "interrupt-names", > + irq_names, irq_count); > + if (!ret) > + return ret; > > - irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq", > - pdata ? pdata->ss_phy_irq_index : -1); > - if (irq > 0) { > - ret = dwc3_qcom_request_irq(qcom, irq, "ss_phy_irq"); > - if (ret) > - return ret; > - qcom->ss_phy_irq = irq; > + for (i = 0; i < irq_count; i++) { > + irq_index = dwc3_qcom_get_irq_index(irq_names[i]); > + if (irq_index == -1) { > + dev_err(&pdev->dev, "Unknown interrupt-name \"%s\" found\n", irq_names[i]); > + continue; > + } > + port_index = dwc3_qcom_get_port_index(irq_names[i], irq_index); > + if (port_index == -1) { > + dev_err(&pdev->dev, "Invalid interrupt-name suffix \"%s\"\n", irq_names[i]); > + continue; > + } > + > + acpi_index = dwc3_qcom_get_acpi_index(qcom, irq_index, port_index); > + > + irq = dwc3_qcom_get_irq(pdev, irq_names[i], acpi_index); > + if (irq > 0) { > + ret = dwc3_qcom_request_irq(qcom, irq, irq_names[i]); > + if (ret) > + return ret; > + > + switch (irq_index) { > + case DP_HS_PHY_IRQ_INDEX: > + qcom->port_info[port_index - 1].dp_hs_phy_irq = irq; > + break; > + case DM_HS_PHY_IRQ_INDEX: > + qcom->port_info[port_index - 1].dm_hs_phy_irq = irq; > + break; > + case SS_PHY_IRQ_INDEX: > + qcom->port_info[port_index - 1].ss_phy_irq = irq; > + break; > + case QUSB2_PHY_IRQ_INDEX: > + qcom->qusb2_phy_irq = irq; > + break; > + } > + > + if (qcom->num_ports < port_index) > + qcom->num_ports = port_index; > + } > } > > return 0; > @@ -1053,20 +1159,14 @@ static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { > .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, > .qscratch_base_size = SDM845_QSCRATCH_SIZE, > .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, > - .qusb2_phy_irq_index = 1, > - .dp_hs_phy_irq_index = 4, > - .dm_hs_phy_irq_index = 3, > - .ss_phy_irq_index = 2 > + .phy_irq_index = {4, 3, 2, 1}, > }; > > static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = { > .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, > .qscratch_base_size = SDM845_QSCRATCH_SIZE, > .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, > - .qusb2_phy_irq_index = 1, > - .dp_hs_phy_irq_index = 4, > - .dm_hs_phy_irq_index = 3, > - .ss_phy_irq_index = 2, > + .phy_irq_index = {4, 3, 2, 1}, > .is_urs = true, > }; > > -- > 2.34.1 > Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> BR, Thinh
On Tue, Feb 06, 2024, Krishna Kurapati wrote: > Power event IRQ stat registers are present for each port > connected to controller. Add support for modifying all power event > irq stat registers present in wrapper. > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > --- > drivers/usb/dwc3/dwc3-qcom.c | 30 +++++++++++++++++++++++------- > 1 file changed, 23 insertions(+), 7 deletions(-) > > diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c > index 572dc3fdae12..e789745a9468 100644 > --- a/drivers/usb/dwc3/dwc3-qcom.c > +++ b/drivers/usb/dwc3/dwc3-qcom.c > @@ -37,7 +37,11 @@ > #define PIPE3_PHYSTATUS_SW BIT(3) > #define PIPE_UTMI_CLK_DIS BIT(8) > > -#define PWR_EVNT_IRQ_STAT_REG 0x58 > +#define PWR_EVNT_IRQ1_STAT_REG 0x58 > +#define PWR_EVNT_IRQ2_STAT_REG 0x1dc > +#define PWR_EVNT_IRQ3_STAT_REG 0x228 > +#define PWR_EVNT_IRQ4_STAT_REG 0x238 > + > #define PWR_EVNT_LPM_IN_L2_MASK BIT(4) > #define PWR_EVNT_LPM_OUT_L2_MASK BIT(5) > > @@ -109,6 +113,13 @@ struct dwc3_qcom { > u8 num_ports; > }; > > +static const u32 pwr_evnt_irq_stat_reg_offset[DWC3_MAX_PORTS] = { > + PWR_EVNT_IRQ1_STAT_REG, > + PWR_EVNT_IRQ2_STAT_REG, > + PWR_EVNT_IRQ3_STAT_REG, > + PWR_EVNT_IRQ4_STAT_REG, > +}; > + > static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) > { > u32 reg; > @@ -444,9 +455,11 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) > if (qcom->is_suspended) > return 0; > > - val = readl(qcom->qscratch_base + PWR_EVNT_IRQ_STAT_REG); > - if (!(val & PWR_EVNT_LPM_IN_L2_MASK)) > - dev_err(qcom->dev, "HS-PHY not in L2\n"); > + for (i = 0; i < qcom->num_ports; i++) { > + val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg_offset[i]); > + if (!(val & PWR_EVNT_LPM_IN_L2_MASK)) > + dev_err(qcom->dev, "Port-%d HS-PHY not in L2\n", i + 1); > + } > > for (i = qcom->num_clocks - 1; i >= 0; i--) > clk_disable_unprepare(qcom->clks[i]); > @@ -491,9 +504,12 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) > if (ret) > dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret); > > - /* Clear existing events from PHY related to L2 in/out */ > - dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG, > - PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); > + for (i = 0; i < qcom->num_ports; i++) { > + /* Clear existing events from PHY related to L2 in/out */ > + dwc3_qcom_setbits(qcom->qscratch_base, > + pwr_evnt_irq_stat_reg_offset[i], > + PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); > + } > > qcom->is_suspended = false; > > -- > 2.34.1 > Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> BR, Thinh