2022-12-29 10:29:41

by Jung Daehwan

[permalink] [raw]
Subject: [RFC PATCH v2 1/3] usb: support Samsung Exynos xHCI Controller

Currently, dwc3 invokes just xhci platform driver without any data.
We add xhci node as child of dwc3 node in order to get data from
device tree. It populates "xhci" child by name during initialization
of host. This patch only effects if dwc3 node has a child named "xhci"
not to disturb original path.

We add "samsung,exynos-xhci" compatible in xhci platform driver
to support Exynos SOCs. We introduce roothub wakeup, which uses roothub
as system wakeup source. It needs xhci platform driver to override
roothub ops.

Signed-off-by: Daehwan Jung <[email protected]>
---
drivers/usb/dwc3/drd.c | 7 +++++
drivers/usb/dwc3/host.c | 33 ++++++++++++++++++--
drivers/usb/host/xhci-plat.c | 60 +++++++++++++++++++++++++++++++++++-
drivers/usb/host/xhci.c | 4 +++
drivers/usb/host/xhci.h | 5 +++
5 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 039bf241769a..7263fd7bdf4f 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -496,6 +496,8 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
static int dwc3_setup_role_switch(struct dwc3 *dwc)
{
struct usb_role_switch_desc dwc3_role_switch = {NULL};
+ struct device_node *dwc3_np = dev_of_node(dwc->dev);
+ struct device_node *xhci_np;
u32 mode;

dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev);
@@ -514,6 +516,10 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
if (IS_ERR(dwc->role_sw))
return PTR_ERR(dwc->role_sw);

+ xhci_np = of_get_child_by_name(dwc3_np, "xhci");
+ if (xhci_np)
+ goto populate_skip;
+
if (dwc->dev->of_node) {
/* populate connector entry */
int ret = devm_of_platform_populate(dwc->dev);
@@ -526,6 +532,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
}
}

+populate_skip:
dwc3_set_mode(dwc, mode);
return 0;
}
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index f6f13e7f1ba1..8adfe45d9681 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -9,6 +9,7 @@

#include <linux/irq.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>

#include "core.h"
@@ -68,9 +69,22 @@ int dwc3_host_init(struct dwc3 *dwc)
{
struct property_entry props[4];
struct platform_device *xhci;
+ struct device_node *dwc3_np = dev_of_node(dwc->dev);
+ struct device_node *xhci_np;
int ret, irq;
int prop_idx = 0;

+ xhci_np = of_get_child_by_name(dwc3_np, "xhci");
+ if (xhci_np) {
+ ret = of_platform_populate(dwc3_np, NULL, NULL, dwc->dev);
+ if (ret) {
+ dev_err(dwc->dev, "failed to register xhci - %d\n", ret);
+ goto err;
+ }
+ dwc->xhci = of_find_device_by_node(xhci_np);
+ goto populate_done;
+ }
+
irq = dwc3_host_get_irq(dwc);
if (irq < 0)
return irq;
@@ -126,14 +140,29 @@ int dwc3_host_init(struct dwc3 *dwc)
goto err;
}

+populate_done:
return 0;
+
err:
- platform_device_put(xhci);
+ if (xhci_np)
+ of_platform_depopulate(dwc->dev);
+ else
+ platform_device_put(xhci);
+
return ret;
}

void dwc3_host_exit(struct dwc3 *dwc)
{
- platform_device_unregister(dwc->xhci);
+ struct device_node *dwc3_np = dev_of_node(dwc->dev);
+ struct device_node *xhci_np;
+
+ xhci_np = of_get_child_by_name(dwc3_np, "xhci");
+
+ if (xhci_np)
+ of_platform_depopulate(dwc->dev);
+ else
+ platform_device_unregister(dwc->xhci);
+
dwc->xhci = NULL;
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 5fb55bf19493..0eeaf306076a 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -29,11 +29,15 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;

static int xhci_plat_setup(struct usb_hcd *hcd);
static int xhci_plat_start(struct usb_hcd *hcd);
+static int xhci_plat_bus_suspend(struct usb_hcd *hcd);
+static int xhci_plat_bus_resume(struct usb_hcd *hcd);

static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_plat_priv),
.reset = xhci_plat_setup,
.start = xhci_plat_start,
+ .bus_suspend = xhci_plat_bus_suspend,
+ .bus_resume = xhci_plat_bus_resume,
};

static void xhci_priv_plat_start(struct usb_hcd *hcd)
@@ -86,6 +90,33 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_PLAT | priv->quirks;
}

+static int xhci_plat_bus_suspend(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ if (xhci->quirks & XHCI_ROOTHUB_WAKEUP) {
+ if (hcd == xhci->main_hcd)
+ __pm_relax(xhci->main_wakelock);
+ else
+ __pm_relax(xhci->shared_wakelock);
+ }
+
+ return xhci_bus_suspend(hcd);
+}
+
+static int xhci_plat_bus_resume(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ if (xhci->quirks & XHCI_ROOTHUB_WAKEUP) {
+ if (hcd == xhci->main_hcd)
+ __pm_stay_awake(xhci->main_wakelock);
+ else
+ __pm_stay_awake(xhci->shared_wakelock);
+ }
+ return xhci_bus_resume(hcd);
+}
+
/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
{
@@ -126,6 +157,10 @@ static const struct xhci_plat_priv xhci_plat_brcm = {
.quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS,
};

+static const struct xhci_plat_priv xhci_plat_exynos = {
+ .quirks = XHCI_ROOTHUB_WAKEUP,
+};
+
static const struct of_device_id usb_xhci_of_match[] = {
{
.compatible = "generic-xhci",
@@ -167,6 +202,9 @@ static const struct of_device_id usb_xhci_of_match[] = {
}, {
.compatible = "brcm,bcm7445-xhci",
.data = &xhci_plat_brcm,
+ }, {
+ .compatible = "samsung,exynos-xhci",
+ .data = &xhci_plat_exynos,
},
{},
};
@@ -185,7 +223,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
int irq;
struct xhci_plat_priv *priv = NULL;

-
if (usb_disabled())
return -ENODEV;

@@ -350,6 +387,21 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}

+ if (xhci->quirks & XHCI_ROOTHUB_WAKEUP) {
+ /* Initialization wakelock for HCD */
+ xhci->main_wakelock = wakeup_source_register(&pdev->dev,
+ dev_name(&pdev->dev));
+ __pm_stay_awake(xhci->main_wakelock);
+ device_set_wakeup_enable(&xhci->main_hcd->self.root_hub->dev, true);
+
+ if (xhci->shared_hcd) {
+ xhci->shared_wakelock = wakeup_source_register(&pdev->dev,
+ dev_name(&pdev->dev));
+ __pm_stay_awake(xhci->shared_wakelock);
+ device_set_wakeup_enable(&xhci->shared_hcd->self.root_hub->dev, true);
+ }
+ }
+
device_enable_async_suspend(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);

@@ -398,6 +450,12 @@ static int xhci_plat_remove(struct platform_device *dev)
pm_runtime_get_sync(&dev->dev);
xhci->xhc_state |= XHCI_STATE_REMOVING;

+ if (xhci->quirks & XHCI_ROOTHUB_WAKEUP) {
+ wakeup_source_unregister(xhci->main_wakelock);
+ if (xhci->shared_wakelock)
+ wakeup_source_unregister(xhci->shared_wakelock);
+ }
+
if (shared_hcd) {
usb_remove_hcd(shared_hcd);
xhci->shared_hcd = NULL;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 79d7931c048a..693495054001 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5502,6 +5502,10 @@ void xhci_init_driver(struct hc_driver *drv,
drv->check_bandwidth = over->check_bandwidth;
if (over->reset_bandwidth)
drv->reset_bandwidth = over->reset_bandwidth;
+ if (over->bus_suspend)
+ drv->bus_suspend = over->bus_suspend;
+ if (over->bus_resume)
+ drv->bus_resume = over->bus_resume;
}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c9f06c5e4e9d..cb9c54a6a22c 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1752,6 +1752,8 @@ struct xhci_hub {
struct xhci_hcd {
struct usb_hcd *main_hcd;
struct usb_hcd *shared_hcd;
+ struct wakeup_source *main_wakelock;
+ struct wakeup_source *shared_wakelock;
/* glue to PCI and HCD framework */
struct xhci_cap_regs __iomem *cap_regs;
struct xhci_op_regs __iomem *op_regs;
@@ -1898,6 +1900,7 @@ struct xhci_hcd {
#define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42)
#define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43)
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
+#define XHCI_ROOTHUB_WAKEUP BIT_ULL(45)

unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1943,6 +1946,8 @@ struct xhci_driver_overrides {
struct usb_host_endpoint *ep);
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+ int (*bus_suspend)(struct usb_hcd *hcd);
+ int (*bus_resume)(struct usb_hcd *hcd);
};

#define XHCI_CFC_DELAY 10
--
2.31.1


2022-12-29 10:42:38

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [RFC PATCH v2 1/3] usb: support Samsung Exynos xHCI Controller

On 29/12/2022 10:57, Daehwan Jung wrote:
> Currently, dwc3 invokes just xhci platform driver without any data.
> We add xhci node as child of dwc3 node in order to get data from
> device tree. It populates "xhci" child by name during initialization
> of host. This patch only effects if dwc3 node has a child named "xhci"
> not to disturb original path.
>
> We add "samsung,exynos-xhci" compatible in xhci platform driver

Where? It is not documented.

> to support Exynos SOCs.

That's so not true. You do nothing to support Exynos SoC here. Please
stop pasting incorrect and misleading commit msgs.

> We introduce roothub wakeup, which uses roothub
> as system wakeup source. It needs xhci platform driver to override
> roothub ops.

You did not explain why you introduced wakelocks...


(...)

> if (shared_hcd) {
> usb_remove_hcd(shared_hcd);
> xhci->shared_hcd = NULL;
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 79d7931c048a..693495054001 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -5502,6 +5502,10 @@ void xhci_init_driver(struct hc_driver *drv,
> drv->check_bandwidth = over->check_bandwidth;
> if (over->reset_bandwidth)
> drv->reset_bandwidth = over->reset_bandwidth;
> + if (over->bus_suspend)
> + drv->bus_suspend = over->bus_suspend;
> + if (over->bus_resume)
> + drv->bus_resume = over->bus_resume;
> }
> }
> EXPORT_SYMBOL_GPL(xhci_init_driver);
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index c9f06c5e4e9d..cb9c54a6a22c 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1752,6 +1752,8 @@ struct xhci_hub {
> struct xhci_hcd {
> struct usb_hcd *main_hcd;
> struct usb_hcd *shared_hcd;
> + struct wakeup_source *main_wakelock;
> + struct wakeup_source *shared_wakelock;

Drop wakelocks. This is not related to USB and not needed here. Do you
see anywhere else in core kernel code usage of the wakelocks?

You got this comment already, didn't you? So why you do not address it?

Best regards,
Krzysztof

2022-12-29 14:55:17

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH v2 1/3] usb: support Samsung Exynos xHCI Controller

On Thu, Dec 29, 2022, at 10:57, Daehwan Jung wrote:
> Currently, dwc3 invokes just xhci platform driver without any data.
> We add xhci node as child of dwc3 node in order to get data from
> device tree. It populates "xhci" child by name during initialization
> of host. This patch only effects if dwc3 node has a child named "xhci"
> not to disturb original path.

Using child nodes is not the normal way of abstracting a soc specific
variant of a device, though there are some USB host drivers that
do this. Just use the node itself and add whatever samsung specific
properties are needed based on the compatible string.

> @@ -86,6 +90,33 @@ static void xhci_plat_quirks(struct device *dev,
> struct xhci_hcd *xhci)
> xhci->quirks |= XHCI_PLAT | priv->quirks;
> }
>
> +static int xhci_plat_bus_suspend(struct usb_hcd *hcd)
> +{
> + struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> +
> + if (xhci->quirks & XHCI_ROOTHUB_WAKEUP) {
> + if (hcd == xhci->main_hcd)
> + __pm_relax(xhci->main_wakelock);
> + else
> + __pm_relax(xhci->shared_wakelock);
> + }
> +
> + return xhci_bus_suspend(hcd);
> +}
> +
> +static int xhci_plat_bus_resume(struct usb_hcd *hcd)
> +{
> + struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> +
> + if (xhci->quirks & XHCI_ROOTHUB_WAKEUP) {
> + if (hcd == xhci->main_hcd)
> + __pm_stay_awake(xhci->main_wakelock);
> + else
> + __pm_stay_awake(xhci->shared_wakelock);
> + }
> + return xhci_bus_resume(hcd);
> +}

It looks like these are no longer tied to the Samsung
device type, which would be a step in the right direction,
but I think adding this should be a separate patch since
it is not a hardware specific change but a new feature.

Arnd

2023-01-02 08:55:29

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [RFC PATCH v2 1/3] usb: support Samsung Exynos xHCI Controller

On 02/01/2023 07:24, Jung Daehwan wrote:
> On Thu, Dec 29, 2022 at 11:25:58AM +0100, Krzysztof Kozlowski wrote:
>> On 29/12/2022 10:57, Daehwan Jung wrote:
>>> Currently, dwc3 invokes just xhci platform driver without any data.
>>> We add xhci node as child of dwc3 node in order to get data from
>>> device tree. It populates "xhci" child by name during initialization
>>> of host. This patch only effects if dwc3 node has a child named "xhci"
>>> not to disturb original path.
>>>
>>> We add "samsung,exynos-xhci" compatible in xhci platform driver
>>
>> Where? It is not documented.
>
> I submitted the patch of dt bindings on same patchset.
> Is there any missing documentation?

This is your first patch in the series and in this patch there is no
such bindings. Re-order the patches to have proper order.

>
>>
>>> to support Exynos SOCs.
>>
>> That's so not true. You do nothing to support Exynos SoC here. Please
>> stop pasting incorrect and misleading commit msgs.
>
> I agree misleading commit msgs. I will modify it.
>
>>
>>> We introduce roothub wakeup, which uses roothub
>>> as system wakeup source. It needs xhci platform driver to override
>>> roothub ops.
>>
>> You did not explain why you introduced wakelocks...
>>
>
> I'm sorry I didn't write description enough.
> I add it below.
>
>>
>> (...)
>>
>>> if (shared_hcd) {
>>> usb_remove_hcd(shared_hcd);
>>> xhci->shared_hcd = NULL;
>>> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
>>> index 79d7931c048a..693495054001 100644
>>> --- a/drivers/usb/host/xhci.c
>>> +++ b/drivers/usb/host/xhci.c
>>> @@ -5502,6 +5502,10 @@ void xhci_init_driver(struct hc_driver *drv,
>>> drv->check_bandwidth = over->check_bandwidth;
>>> if (over->reset_bandwidth)
>>> drv->reset_bandwidth = over->reset_bandwidth;
>>> + if (over->bus_suspend)
>>> + drv->bus_suspend = over->bus_suspend;
>>> + if (over->bus_resume)
>>> + drv->bus_resume = over->bus_resume;
>>> }
>>> }
>>> EXPORT_SYMBOL_GPL(xhci_init_driver);
>>> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
>>> index c9f06c5e4e9d..cb9c54a6a22c 100644
>>> --- a/drivers/usb/host/xhci.h
>>> +++ b/drivers/usb/host/xhci.h
>>> @@ -1752,6 +1752,8 @@ struct xhci_hub {
>>> struct xhci_hcd {
>>> struct usb_hcd *main_hcd;
>>> struct usb_hcd *shared_hcd;
>>> + struct wakeup_source *main_wakelock;
>>> + struct wakeup_source *shared_wakelock;
>>
>> Drop wakelocks. This is not related to USB and not needed here. Do you
>> see anywhere else in core kernel code usage of the wakelocks?
>>
>> You got this comment already, didn't you? So why you do not address it?
>>
>
> I want to add a new feature in xhci platform driver. I want to make it
> possible to enter system sleep while usb host connected like USB Mouse.
> It gets system enter sleep only if there's no usb transaction at all.
> Deciding if there's tranaction or not is in root hub because it's parent
> of all child usb devices.


I have USB mouse connected to my system and the system enters suspend,
thus I don't think this patch solves this particular issue.

Best regards,
Krzysztof