In the system I'm working on, there is an always-on subsystem which
includes a small size memory, and several functions need to run and
occupy the memory from the small memory if they need to run on the
always-on subsystem. These functions must allocate the memory from the
small memory region, so that they can get benefit from the always-on
subsystem. So the small memory is split for multiple functions which are
satisfied with their generic use cases. But in specific use cases, like
USB3 devices which support the stream trasnsfer or multiple devices
connect to the host, they required more memory than their pre-allocated
memory region. I tried to implement it in a generic way and propose this
patch to give it the ability to get the memory from the other larger
memory to solve the issue.
Changelog
--------------------------------------------
Changes in v5:
- Fix build break.
- Use of_property_count_u32_elems() instead of
of_property_count_elems_of_size().
Changes in v4:
- Add the driver where uses the multiple coherent memory regions.
Changes in v3:
- Re-org the members of struct dma_coherent_mem to avoid additional
pointer arithmetics and the holes inside the structure.
- Use consistent naming of return value.
- Re-write the dev checking statement to be more clear.
Changes in v2:
- Replace the pointer(dma_mem) to a list_head(dma_mems) in the device
structure and initialize the list_head in device_initialize().
- Modify the required changes in coherent.c.
Howard Yen (2):
dma-coherent: add support for multi coherent rmems per dev
usb: host: xhci-plat: add support for multi memory regions
drivers/base/core.c | 3 ++
drivers/usb/host/xhci-plat.c | 19 +++++++-
include/linux/device.h | 5 +-
kernel/dma/coherent.c | 92 +++++++++++++++++++++++-------------
4 files changed, 82 insertions(+), 37 deletions(-)
--
2.44.0.278.ge034bb2e1d-goog
Add support for multiple coherent rmems per device. This patch replaces
original dma_mem with dma_mems list in device structure to store multiple
rmems.
These multiple rmems can be assigned to the device one by one by
of_reserved_mem_device_init_by_idx() with the memory-region
declaration in device tree as below and store the rmem to the dma_mems
list.
device1@0 {
...
memory-region = <&reserved_mem0>, <&reserved_mem1>;
...
};
When driver tries to allocate memory from the rmems, looks for the first
available rmem and allocates the memory from this rmem.
Then if driver removed, of_reserved_mem_device_release() needs to be
invoked to release all the rmems assigned to the device.
Signed-off-by: Howard Yen <[email protected]>
---
drivers/base/core.c | 3 ++
include/linux/device.h | 5 +--
kernel/dma/coherent.c | 92 +++++++++++++++++++++++++++---------------
3 files changed, 64 insertions(+), 36 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 14d46af40f9a..d9af38d7b870 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3108,6 +3108,9 @@ void device_initialize(struct device *dev)
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
dev->dma_coherent = dma_default_coherent;
+#endif
+#ifdef CONFIG_DMA_DECLARE_COHERENT
+ INIT_LIST_HEAD(&dev->dma_mems);
#endif
swiotlb_dev_init(dev);
}
diff --git a/include/linux/device.h b/include/linux/device.h
index 97c4b046c09d..5fa15e5adbdc 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -648,7 +648,7 @@ struct device_physical_location {
* @dma_parms: A low level driver may set these to teach IOMMU code about
* segment limitations.
* @dma_pools: Dma pools (if dma'ble device).
- * @dma_mem: Internal for coherent mem override.
+ * @dma_mems: Internal for coherent mems.
* @cma_area: Contiguous memory area for dma allocations
* @dma_io_tlb_mem: Software IO TLB allocator. Not for driver use.
* @dma_io_tlb_pools: List of transient swiotlb memory pools.
@@ -749,8 +749,7 @@ struct device {
struct list_head dma_pools; /* dma pools (if dma'ble) */
#ifdef CONFIG_DMA_DECLARE_COHERENT
- struct dma_coherent_mem *dma_mem; /* internal for coherent mem
- override */
+ struct list_head dma_mems; /* Internal for coherent mems */
#endif
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index ff5683a57f77..f6748a3a5eb1 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -11,22 +11,16 @@
#include <linux/dma-map-ops.h>
struct dma_coherent_mem {
- void *virt_base;
- dma_addr_t device_base;
- unsigned long pfn_base;
- int size;
- unsigned long *bitmap;
- spinlock_t spinlock;
- bool use_dev_dma_pfn_offset;
+ struct list_head node;
+ void *virt_base;
+ dma_addr_t device_base;
+ unsigned long pfn_base;
+ int size;
+ spinlock_t spinlock;
+ unsigned long *bitmap;
+ bool use_dev_dma_pfn_offset;
};
-static inline struct dma_coherent_mem *dev_get_coherent_memory(struct device *dev)
-{
- if (dev && dev->dma_mem)
- return dev->dma_mem;
- return NULL;
-}
-
static inline dma_addr_t dma_get_device_base(struct device *dev,
struct dma_coherent_mem * mem)
{
@@ -61,6 +55,7 @@ static struct dma_coherent_mem *dma_init_coherent_memory(phys_addr_t phys_addr,
dma_mem->pfn_base = PFN_DOWN(phys_addr);
dma_mem->size = pages;
dma_mem->use_dev_dma_pfn_offset = use_dma_pfn_offset;
+ INIT_LIST_HEAD(&dma_mem->node);
spin_lock_init(&dma_mem->spinlock);
return dma_mem;
@@ -90,10 +85,8 @@ static int dma_assign_coherent_memory(struct device *dev,
if (!dev)
return -ENODEV;
- if (dev->dma_mem)
- return -EBUSY;
+ list_add_tail(&mem->node, &dev->dma_mems);
- dev->dma_mem = mem;
return 0;
}
@@ -118,23 +111,28 @@ int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr,
dma_addr_t device_addr, size_t size)
{
struct dma_coherent_mem *mem;
- int ret;
+ int retval;
mem = dma_init_coherent_memory(phys_addr, device_addr, size, false);
if (IS_ERR(mem))
return PTR_ERR(mem);
- ret = dma_assign_coherent_memory(dev, mem);
- if (ret)
+ retval = dma_assign_coherent_memory(dev, mem);
+ if (retval)
_dma_release_coherent_memory(mem);
- return ret;
+ return retval;
}
void dma_release_coherent_memory(struct device *dev)
{
- if (dev) {
- _dma_release_coherent_memory(dev->dma_mem);
- dev->dma_mem = NULL;
+ struct dma_coherent_mem *mem_tmp, *q;
+
+ if (!dev)
+ return;
+
+ list_for_each_entry_safe(mem_tmp, q, &dev->dma_mems, node) {
+ list_del_init(&mem_tmp->node);
+ _dma_release_coherent_memory(mem_tmp);
}
}
@@ -187,12 +185,17 @@ static void *__dma_alloc_from_coherent(struct device *dev,
int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size,
dma_addr_t *dma_handle, void **ret)
{
- struct dma_coherent_mem *mem = dev_get_coherent_memory(dev);
+ struct dma_coherent_mem *mem_tmp;
- if (!mem)
+ if (list_empty(&dev->dma_mems))
return 0;
- *ret = __dma_alloc_from_coherent(dev, mem, size, dma_handle);
+ list_for_each_entry(mem_tmp, &dev->dma_mems, node) {
+ *ret = __dma_alloc_from_coherent(dev, mem_tmp, size, dma_handle);
+ if (*ret)
+ break;
+ }
+
return 1;
}
@@ -226,9 +229,16 @@ static int __dma_release_from_coherent(struct dma_coherent_mem *mem,
*/
int dma_release_from_dev_coherent(struct device *dev, int order, void *vaddr)
{
- struct dma_coherent_mem *mem = dev_get_coherent_memory(dev);
+ struct dma_coherent_mem *mem_tmp;
+ int retval = 0;
- return __dma_release_from_coherent(mem, order, vaddr);
+ list_for_each_entry(mem_tmp, &dev->dma_mems, node) {
+ retval = __dma_release_from_coherent(mem_tmp, order, vaddr);
+ if (retval == 1)
+ break;
+ }
+
+ return retval;
}
static int __dma_mmap_from_coherent(struct dma_coherent_mem *mem,
@@ -271,9 +281,16 @@ static int __dma_mmap_from_coherent(struct dma_coherent_mem *mem,
int dma_mmap_from_dev_coherent(struct device *dev, struct vm_area_struct *vma,
void *vaddr, size_t size, int *ret)
{
- struct dma_coherent_mem *mem = dev_get_coherent_memory(dev);
+ struct dma_coherent_mem *mem_tmp;
+ int retval = 0;
- return __dma_mmap_from_coherent(mem, vma, vaddr, size, ret);
+ list_for_each_entry(mem_tmp, &dev->dma_mems, node) {
+ retval = __dma_mmap_from_coherent(mem_tmp, vma, vaddr, size, ret);
+ if (retval == 1)
+ break;
+ }
+
+ return retval;
}
#ifdef CONFIG_DMA_GLOBAL_POOL
@@ -351,8 +368,17 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
static void rmem_dma_device_release(struct reserved_mem *rmem,
struct device *dev)
{
- if (dev)
- dev->dma_mem = NULL;
+ struct dma_coherent_mem *mem_tmp, *q;
+
+ if (!dev)
+ return;
+
+ list_for_each_entry_safe(mem_tmp, q, &dev->dma_mems, node) {
+ if (mem_tmp == rmem->priv) {
+ list_del_init(&mem_tmp->node);
+ break;
+ }
+ }
}
static const struct reserved_mem_ops rmem_dma_ops = {
--
2.44.0.278.ge034bb2e1d-goog
The reason why it needs multiple regions is that in my system there is
an always-on subsystem which includes a small size memory, and several
functions need to run and occupy the memory from the small memory if
they need to run on the always-on subsystem. These functions must
allocate the memory from the small memory region, so that they can get
benefit from the always-on subsystem. So the small memory is split for
multiple functions which are satisfied with their generic use cases.
But in specific use cases, like USB3 devices which support the stream
trasnsfer or multiple devices connect to the host, they required more
memory than their pre-allocated memory region, so I tried to propose
this patch to give it the ability to get the memory from the other
larger memory to solve the issue.
Signed-off-by: Howard Yen <[email protected]>
---
drivers/usb/host/xhci-plat.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 3d071b875308..7892d3eb26d2 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -14,6 +14,7 @@
#include <linux/pci.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/usb/phy.h>
#include <linux/slab.h>
@@ -149,7 +150,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
struct xhci_hcd *xhci;
struct resource *res;
struct usb_hcd *hcd, *usb3_hcd;
- int ret;
+ int i, count, ret;
int irq;
struct xhci_plat_priv *priv = NULL;
bool of_match;
@@ -194,6 +195,19 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
xhci->allow_single_roothub = 1;
+ count = of_property_count_u32_elems(sysdev->of_node, "memory-region");
+
+ for (i = 0; i < count; i++) {
+ ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
+ if (ret) {
+ dev_err(sysdev, "Could not get reserved memory\n");
+ if (i > 0)
+ of_reserved_mem_device_release(sysdev);
+
+ return ret;
+ }
+ }
+
/*
* Not all platforms have clks so it is not an error if the
* clock do not exist.
@@ -431,6 +445,9 @@ void xhci_plat_remove(struct platform_device *dev)
clk_disable_unprepare(clk);
clk_disable_unprepare(reg_clk);
reset_control_assert(xhci->reset);
+
+ of_reserved_mem_device_release(hcd->self.sysdev);
+
usb_put_hcd(hcd);
pm_runtime_disable(&dev->dev);
--
2.44.0.278.ge034bb2e1d-goog
On Mon, Mar 11, 2024 at 6:03 PM Andy Shevchenko
<[email protected]> wrote:
>
> On Mon, Mar 11, 2024 at 09:49:45AM +0000, Howard Yen wrote:
> > In the system I'm working on, there is an always-on subsystem which
> > includes a small size memory, and several functions need to run and
> > occupy the memory from the small memory if they need to run on the
> > always-on subsystem. These functions must allocate the memory from the
> > small memory region, so that they can get benefit from the always-on
> > subsystem. So the small memory is split for multiple functions which are
> > satisfied with their generic use cases. But in specific use cases, like
> > USB3 devices which support the stream trasnsfer or multiple devices
> > connect to the host, they required more memory than their pre-allocated
> > memory region. I tried to implement it in a generic way and propose this
> > patch to give it the ability to get the memory from the other larger
> > memory to solve the issue.
>
> > Changelog
> > --------------------------------------------
> > Changes in v5:
> > - Fix build break.
> > - Use of_property_count_u32_elems() instead of
> > of_property_count_elems_of_size().
>
> Have you tried to use --histogram diff algo?
Yes, I used the below command to create the patch v5.
`git format-patch --cover-letter --histogram -v5 51b70ff55`
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
--
Best Regards,
Howard
On Mon, Mar 11, 2024 at 06:10:47PM +0800, Howard Yen wrote:
> On Mon, Mar 11, 2024 at 6:03 PM Andy Shevchenko
> <[email protected]> wrote:
> > On Mon, Mar 11, 2024 at 09:49:45AM +0000, Howard Yen wrote:
..
> > > Changes in v5:
> > > - Fix build break.
> > > - Use of_property_count_u32_elems() instead of
> > > of_property_count_elems_of_size().
> >
> > Have you tried to use --histogram diff algo?
>
> Yes, I used the below command to create the patch v5.
> `git format-patch --cover-letter --histogram -v5 51b70ff55`
Thank you for confirming!
--
With Best Regards,
Andy Shevchenko
On 2024-03-11 9:49 am, Howard Yen wrote:
> The reason why it needs multiple regions is that in my system there is
> an always-on subsystem which includes a small size memory, and several
> functions need to run and occupy the memory from the small memory if
> they need to run on the always-on subsystem. These functions must
> allocate the memory from the small memory region, so that they can get
> benefit from the always-on subsystem. So the small memory is split for
> multiple functions which are satisfied with their generic use cases.
> But in specific use cases, like USB3 devices which support the stream
> trasnsfer or multiple devices connect to the host, they required more
> memory than their pre-allocated memory region, so I tried to propose
> this patch to give it the ability to get the memory from the other
> larger memory to solve the issue.
Once again this still fails to make sense - The USB controller has a
special always-on pool from which it "must allocate", yet it's fine if
it also allocates from elsewhere? How on Earth is that supposed to work?
As I said before, if it's actually the case that only certain specific
allocations (based on driver-level knowledge) must come from the special
pool, then this is not something which can realistically be abstracted
by the generic dma-coherent API as it stands (and if so, do the
non-special allocations even need a dedicated second reserved region, or
is that in fact just a massive hack around the dma_coherent_mem design
intentionally not falling back to the regular allocator?)
Thanks,
Robin.
> Signed-off-by: Howard Yen <[email protected]>
> ---
> drivers/usb/host/xhci-plat.c | 19 ++++++++++++++++++-
> 1 file changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
> index 3d071b875308..7892d3eb26d2 100644
> --- a/drivers/usb/host/xhci-plat.c
> +++ b/drivers/usb/host/xhci-plat.c
> @@ -14,6 +14,7 @@
> #include <linux/pci.h>
> #include <linux/of.h>
> #include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> #include <linux/platform_device.h>
> #include <linux/usb/phy.h>
> #include <linux/slab.h>
> @@ -149,7 +150,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
> struct xhci_hcd *xhci;
> struct resource *res;
> struct usb_hcd *hcd, *usb3_hcd;
> - int ret;
> + int i, count, ret;
> int irq;
> struct xhci_plat_priv *priv = NULL;
> bool of_match;
> @@ -194,6 +195,19 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
>
> xhci->allow_single_roothub = 1;
>
> + count = of_property_count_u32_elems(sysdev->of_node, "memory-region");
> +
> + for (i = 0; i < count; i++) {
> + ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
> + if (ret) {
> + dev_err(sysdev, "Could not get reserved memory\n");
> + if (i > 0)
> + of_reserved_mem_device_release(sysdev);
> +
> + return ret;
> + }
> + }
> +
> /*
> * Not all platforms have clks so it is not an error if the
> * clock do not exist.
> @@ -431,6 +445,9 @@ void xhci_plat_remove(struct platform_device *dev)
> clk_disable_unprepare(clk);
> clk_disable_unprepare(reg_clk);
> reset_control_assert(xhci->reset);
> +
> + of_reserved_mem_device_release(hcd->self.sysdev);
> +
> usb_put_hcd(hcd);
>
> pm_runtime_disable(&dev->dev);
On Mon, Mar 11, 2024 at 09:49:45AM +0000, Howard Yen wrote:
> In the system I'm working on, there is an always-on subsystem which
> includes a small size memory, and several functions need to run and
> occupy the memory from the small memory if they need to run on the
> always-on subsystem. These functions must allocate the memory from the
> small memory region, so that they can get benefit from the always-on
> subsystem. So the small memory is split for multiple functions which are
> satisfied with their generic use cases. But in specific use cases, like
> USB3 devices which support the stream trasnsfer or multiple devices
> connect to the host, they required more memory than their pre-allocated
> memory region. I tried to implement it in a generic way and propose this
> patch to give it the ability to get the memory from the other larger
> memory to solve the issue.
> Changelog
> --------------------------------------------
> Changes in v5:
> - Fix build break.
> - Use of_property_count_u32_elems() instead of
> of_property_count_elems_of_size().
Have you tried to use --histogram diff algo?
--
With Best Regards,
Andy Shevchenko
Hi Howard,
kernel test robot noticed the following build warnings:
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Howard-Yen/dma-coherent-add-support-for-multi-coherent-rmems-per-dev/20240311-175308
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/20240311094947.3738200-3-howardyen%40google.com
patch subject: [PATCH v5 2/2] usb: host: xhci-plat: add support for multi memory regions
config: riscv-randconfig-r081-20240311 (https://download.01.org/0day-ci/archive/20240313/[email protected]/config)
compiler: riscv64-linux-gcc (GCC) 13.2.0
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Reported-by: Dan Carpenter <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/
New smatch warnings:
drivers/usb/host/xhci-plat.c:207 xhci_plat_probe() warn: missing unwind goto?
vim +207 drivers/usb/host/xhci-plat.c
ec5499d338ece9 Arnd Bergmann 2023-01-31 146 int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match)
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 147 {
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 148 const struct hc_driver *driver;
ec5499d338ece9 Arnd Bergmann 2023-01-31 149 struct device *tmpdev;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 150 struct xhci_hcd *xhci;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 151 struct resource *res;
e0fe986972f5b6 Heiner Kallweit 2022-05-12 152 struct usb_hcd *hcd, *usb3_hcd;
fe4daa605d0cae Howard Yen 2024-03-11 153 int i, count, ret;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 154 int irq;
f768e718911e03 Peter Chen 2020-09-18 155 struct xhci_plat_priv *priv = NULL;
16b7e0cccb2430 Johan Hovold 2023-11-03 156 bool of_match;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 157
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 158 if (usb_disabled())
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 159 return -ENODEV;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 160
1885d9a33753b7 Andrew Bresticker 2014-10-03 161 driver = &xhci_plat_hc_driver;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 162
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 163 irq = platform_get_irq(pdev, 0);
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 164 if (irq < 0)
4b148d5144d64e Thomas Petazzoni 2017-05-17 165 return irq;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 166
c6b8e79306f515 Adam Wallis 2017-09-18 167 if (!sysdev)
c6b8e79306f515 Adam Wallis 2017-09-18 168 sysdev = &pdev->dev;
4c39d4b949d36f Arnd Bergmann 2017-03-13 169
4c39d4b949d36f Arnd Bergmann 2017-03-13 170 ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
c10cf1189d7659 Xenia Ragiadakou 2013-08-14 171 if (ret)
c10cf1189d7659 Xenia Ragiadakou 2013-08-14 172 return ret;
c10cf1189d7659 Xenia Ragiadakou 2013-08-14 173
b0c69b4bace370 Baolin Wang 2017-04-19 174 pm_runtime_set_active(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 175 pm_runtime_enable(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 176 pm_runtime_get_noresume(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 177
4c39d4b949d36f Arnd Bergmann 2017-03-13 178 hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
4c39d4b949d36f Arnd Bergmann 2017-03-13 179 dev_name(&pdev->dev), NULL);
b0c69b4bace370 Baolin Wang 2017-04-19 180 if (!hcd) {
b0c69b4bace370 Baolin Wang 2017-04-19 181 ret = -ENOMEM;
b0c69b4bace370 Baolin Wang 2017-04-19 182 goto disable_runtime;
b0c69b4bace370 Baolin Wang 2017-04-19 183 }
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 184
fb222273a2159a Dejin Zheng 2020-03-24 185 hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
fd666348c51f6a Himangi Saraogi 2014-06-20 186 if (IS_ERR(hcd->regs)) {
fd666348c51f6a Himangi Saraogi 2014-06-20 187 ret = PTR_ERR(hcd->regs);
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 188 goto put_hcd;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 189 }
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 190
e7020f193a3d13 Varka Bhadram 2014-11-04 191 hcd->rsrc_start = res->start;
e7020f193a3d13 Varka Bhadram 2014-11-04 192 hcd->rsrc_len = resource_size(res);
e7020f193a3d13 Varka Bhadram 2014-11-04 193
08048c04cc6f75 Chunfeng Yun 2019-04-17 194 xhci = hcd_to_xhci(hcd);
08048c04cc6f75 Chunfeng Yun 2019-04-17 195
4736ebd7fcaff1 Heiner Kallweit 2022-05-12 196 xhci->allow_single_roothub = 1;
4736ebd7fcaff1 Heiner Kallweit 2022-05-12 197
fe4daa605d0cae Howard Yen 2024-03-11 198 count = of_property_count_u32_elems(sysdev->of_node, "memory-region");
fe4daa605d0cae Howard Yen 2024-03-11 199
fe4daa605d0cae Howard Yen 2024-03-11 200 for (i = 0; i < count; i++) {
fe4daa605d0cae Howard Yen 2024-03-11 201 ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
fe4daa605d0cae Howard Yen 2024-03-11 202 if (ret) {
fe4daa605d0cae Howard Yen 2024-03-11 203 dev_err(sysdev, "Could not get reserved memory\n");
fe4daa605d0cae Howard Yen 2024-03-11 204 if (i > 0)
fe4daa605d0cae Howard Yen 2024-03-11 205 of_reserved_mem_device_release(sysdev);
fe4daa605d0cae Howard Yen 2024-03-11 206
fe4daa605d0cae Howard Yen 2024-03-11 @207 return ret;
Needs to clean up before returning.
fe4daa605d0cae Howard Yen 2024-03-11 208 }
fe4daa605d0cae Howard Yen 2024-03-11 209 }
fe4daa605d0cae Howard Yen 2024-03-11 210
4718c177405112 Gregory CLEMENT 2014-05-15 211 /*
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 212 * Not all platforms have clks so it is not an error if the
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 213 * clock do not exist.
4718c177405112 Gregory CLEMENT 2014-05-15 214 */
08048c04cc6f75 Chunfeng Yun 2019-04-17 215 xhci->reg_clk = devm_clk_get_optional(&pdev->dev, "reg");
08048c04cc6f75 Chunfeng Yun 2019-04-17 216 if (IS_ERR(xhci->reg_clk)) {
08048c04cc6f75 Chunfeng Yun 2019-04-17 217 ret = PTR_ERR(xhci->reg_clk);
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 218 goto put_hcd;
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 219 }
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 220
08048c04cc6f75 Chunfeng Yun 2019-04-17 221 xhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
08048c04cc6f75 Chunfeng Yun 2019-04-17 222 if (IS_ERR(xhci->clk)) {
08048c04cc6f75 Chunfeng Yun 2019-04-17 223 ret = PTR_ERR(xhci->clk);
8c6e8b09617915 Biju Das 2023-01-21 224 goto put_hcd;
4718c177405112 Gregory CLEMENT 2014-05-15 225 }
4718c177405112 Gregory CLEMENT 2014-05-15 226
224eb5311d6a8c Biju Das 2023-01-21 227 xhci->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
224eb5311d6a8c Biju Das 2023-01-21 228 if (IS_ERR(xhci->reset)) {
224eb5311d6a8c Biju Das 2023-01-21 229 ret = PTR_ERR(xhci->reset);
224eb5311d6a8c Biju Das 2023-01-21 230 goto put_hcd;
224eb5311d6a8c Biju Das 2023-01-21 231 }
224eb5311d6a8c Biju Das 2023-01-21 232
224eb5311d6a8c Biju Das 2023-01-21 233 ret = reset_control_deassert(xhci->reset);
8c6e8b09617915 Biju Das 2023-01-21 234 if (ret)
8c6e8b09617915 Biju Das 2023-01-21 235 goto put_hcd;
8c6e8b09617915 Biju Das 2023-01-21 236
224eb5311d6a8c Biju Das 2023-01-21 237 ret = clk_prepare_enable(xhci->reg_clk);
224eb5311d6a8c Biju Das 2023-01-21 238 if (ret)
224eb5311d6a8c Biju Das 2023-01-21 239 goto err_reset;
224eb5311d6a8c Biju Das 2023-01-21 240
08048c04cc6f75 Chunfeng Yun 2019-04-17 241 ret = clk_prepare_enable(xhci->clk);
08048c04cc6f75 Chunfeng Yun 2019-04-17 242 if (ret)
08048c04cc6f75 Chunfeng Yun 2019-04-17 243 goto disable_reg_clk;
08048c04cc6f75 Chunfeng Yun 2019-04-17 244
2847d242a1e48c Geert Uytterhoeven 2017-10-05 245 if (priv_match) {
f768e718911e03 Peter Chen 2020-09-18 246 priv = hcd_to_xhci_priv(hcd);
4efb2f69411456 Yoshihiro Shimoda 2015-11-24 247 /* Just copy data for now */
4efb2f69411456 Yoshihiro Shimoda 2015-11-24 248 *priv = *priv_match;
4efb2f69411456 Yoshihiro Shimoda 2015-11-24 249 }
4efb2f69411456 Yoshihiro Shimoda 2015-11-24 250
4bb4fc0dbfa23a Peter Chen 2020-09-18 251 device_set_wakeup_capable(&pdev->dev, true);
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 252
9fa733f24bb54d Roger Quadros 2015-05-29 253 xhci->main_hcd = hcd;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 254
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 255 /* imod_interval is the interrupt moderation value in nanoseconds. */
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 256 xhci->imod_interval = 40000;
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 257
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 258 /* Iterate over all parent nodes for finding quirks */
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 259 for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 260
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 261 if (device_property_read_bool(tmpdev, "usb2-lpm-disable"))
4750bc78efdb12 Thang Q. Nguyen 2017-10-05 262 xhci->quirks |= XHCI_HW_LPM_DISABLE;
4750bc78efdb12 Thang Q. Nguyen 2017-10-05 263
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 264 if (device_property_read_bool(tmpdev, "usb3-lpm-capable"))
20f6fdd01c2c0d Pratyush Anand 2014-07-04 265 xhci->quirks |= XHCI_LPM_SUPPORT;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 266
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 267 if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
21939f003ad093 Felipe Balbi 2017-01-23 268 xhci->quirks |= XHCI_BROKEN_PORT_PED;
21939f003ad093 Felipe Balbi 2017-01-23 269
520b391e3e813c Prashanth K 2024-01-16 270 if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
520b391e3e813c Prashanth K 2024-01-16 271 xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
520b391e3e813c Prashanth K 2024-01-16 272
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 273 device_property_read_u32(tmpdev, "imod-interval-ns",
ab725cbec3e83d Adam Wallis 2017-12-08 274 &xhci->imod_interval);
222471f7640d97 Anurag Kumar Vulisha 2018-08-31 275 }
ab725cbec3e83d Adam Wallis 2017-12-08 276
16b7e0cccb2430 Johan Hovold 2023-11-03 277 /*
16b7e0cccb2430 Johan Hovold 2023-11-03 278 * Drivers such as dwc3 manages PHYs themself (and rely on driver name
16b7e0cccb2430 Johan Hovold 2023-11-03 279 * matching for the xhci platform device).
16b7e0cccb2430 Johan Hovold 2023-11-03 280 */
16b7e0cccb2430 Johan Hovold 2023-11-03 281 of_match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
16b7e0cccb2430 Johan Hovold 2023-11-03 282 if (of_match) {
4c39d4b949d36f Arnd Bergmann 2017-03-13 283 hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
7b8ef22ea547b8 Maxime Ripard 2015-03-17 284 if (IS_ERR(hcd->usb_phy)) {
7b8ef22ea547b8 Maxime Ripard 2015-03-17 285 ret = PTR_ERR(hcd->usb_phy);
7b8ef22ea547b8 Maxime Ripard 2015-03-17 286 if (ret == -EPROBE_DEFER)
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 287 goto disable_clk;
7b8ef22ea547b8 Maxime Ripard 2015-03-17 288 hcd->usb_phy = NULL;
7b8ef22ea547b8 Maxime Ripard 2015-03-17 289 } else {
7b8ef22ea547b8 Maxime Ripard 2015-03-17 290 ret = usb_phy_init(hcd->usb_phy);
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 291 if (ret)
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 292 goto disable_clk;
7b8ef22ea547b8 Maxime Ripard 2015-03-17 293 }
16b7e0cccb2430 Johan Hovold 2023-11-03 294 }
7b8ef22ea547b8 Maxime Ripard 2015-03-17 295
c94d41e9dd1ba3 Peter Chen 2018-09-20 296 hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 297
8e10548f7f4814 Pali Roh?r 2022-02-03 298 if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
f768e718911e03 Peter Chen 2020-09-18 299 hcd->skip_phy_initialization = 1;
f768e718911e03 Peter Chen 2020-09-18 300
bac1ec55143469 Tejas Joglekar 2020-12-08 301 if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
bac1ec55143469 Tejas Joglekar 2020-12-08 302 xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
bac1ec55143469 Tejas Joglekar 2020-12-08 303
4ac53087d6d48e Roger Quadros 2015-05-29 304 ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
7b8ef22ea547b8 Maxime Ripard 2015-03-17 305 if (ret)
7b8ef22ea547b8 Maxime Ripard 2015-03-17 306 goto disable_usb_phy;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 307
e0fe986972f5b6 Heiner Kallweit 2022-05-12 308 if (!xhci_has_one_roothub(xhci)) {
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 309 xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 310 dev_name(&pdev->dev), hcd);
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 311 if (!xhci->shared_hcd) {
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 312 ret = -ENOMEM;
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 313 goto dealloc_usb2_hcd;
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 314 }
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 315
16b7e0cccb2430 Johan Hovold 2023-11-03 316 if (of_match) {
9134c1fd05034d Stanley Chang 2023-04-07 317 xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev,
9134c1fd05034d Stanley Chang 2023-04-07 318 "usb-phy", 1);
9134c1fd05034d Stanley Chang 2023-04-07 319 if (IS_ERR(xhci->shared_hcd->usb_phy)) {
9134c1fd05034d Stanley Chang 2023-04-07 320 xhci->shared_hcd->usb_phy = NULL;
9134c1fd05034d Stanley Chang 2023-04-07 321 } else {
9134c1fd05034d Stanley Chang 2023-04-07 322 ret = usb_phy_init(xhci->shared_hcd->usb_phy);
9134c1fd05034d Stanley Chang 2023-04-07 323 if (ret)
9134c1fd05034d Stanley Chang 2023-04-07 324 dev_err(sysdev, "%s init usb3phy fail (ret=%d)\n",
9134c1fd05034d Stanley Chang 2023-04-07 325 __func__, ret);
9134c1fd05034d Stanley Chang 2023-04-07 326 }
16b7e0cccb2430 Johan Hovold 2023-11-03 327 }
9134c1fd05034d Stanley Chang 2023-04-07 328
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 329 xhci->shared_hcd->tpl_support = hcd->tpl_support;
e0fe986972f5b6 Heiner Kallweit 2022-05-12 330 }
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 331
e0fe986972f5b6 Heiner Kallweit 2022-05-12 332 usb3_hcd = xhci_get_usb3_hcd(xhci);
e0fe986972f5b6 Heiner Kallweit 2022-05-12 333 if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4)
e0fe986972f5b6 Heiner Kallweit 2022-05-12 334 usb3_hcd->can_do_streams = 1;
5de4e1ea9a731c William wu 2017-01-17 335
e0fe986972f5b6 Heiner Kallweit 2022-05-12 336 if (xhci->shared_hcd) {
4ac53087d6d48e Roger Quadros 2015-05-29 337 ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
4ac53087d6d48e Roger Quadros 2015-05-29 338 if (ret)
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 339 goto put_usb3_hcd;
e0fe986972f5b6 Heiner Kallweit 2022-05-12 340 }
4ac53087d6d48e Roger Quadros 2015-05-29 341
c70a1529b29cb1 Andrew Bresticker 2017-04-07 342 device_enable_async_suspend(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 343 pm_runtime_put_noidle(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 344
b0c69b4bace370 Baolin Wang 2017-04-19 345 /*
b0c69b4bace370 Baolin Wang 2017-04-19 346 * Prevent runtime pm from being on as default, users should enable
b0c69b4bace370 Baolin Wang 2017-04-19 347 * runtime pm using power/control in sysfs.
b0c69b4bace370 Baolin Wang 2017-04-19 348 */
b0c69b4bace370 Baolin Wang 2017-04-19 349 pm_runtime_forbid(&pdev->dev);
c70a1529b29cb1 Andrew Bresticker 2017-04-07 350
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 351 return 0;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 352
4ac53087d6d48e Roger Quadros 2015-05-29 353
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 354 put_usb3_hcd:
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 355 usb_put_hcd(xhci->shared_hcd);
0cf1ea040a7e2c Heiner Kallweit 2022-05-12 356
4ac53087d6d48e Roger Quadros 2015-05-29 357 dealloc_usb2_hcd:
4ac53087d6d48e Roger Quadros 2015-05-29 358 usb_remove_hcd(hcd);
4ac53087d6d48e Roger Quadros 2015-05-29 359
7b8ef22ea547b8 Maxime Ripard 2015-03-17 360 disable_usb_phy:
7b8ef22ea547b8 Maxime Ripard 2015-03-17 361 usb_phy_shutdown(hcd->usb_phy);
7b8ef22ea547b8 Maxime Ripard 2015-03-17 362
4718c177405112 Gregory CLEMENT 2014-05-15 363 disable_clk:
08048c04cc6f75 Chunfeng Yun 2019-04-17 364 clk_disable_unprepare(xhci->clk);
4718c177405112 Gregory CLEMENT 2014-05-15 365
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 366 disable_reg_clk:
08048c04cc6f75 Chunfeng Yun 2019-04-17 367 clk_disable_unprepare(xhci->reg_clk);
3ae2da7b28b393 Gregory CLEMENT 2018-04-20 368
224eb5311d6a8c Biju Das 2023-01-21 369 err_reset:
224eb5311d6a8c Biju Das 2023-01-21 370 reset_control_assert(xhci->reset);
224eb5311d6a8c Biju Das 2023-01-21 371
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 372 put_hcd:
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 373 usb_put_hcd(hcd);
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 374
b0c69b4bace370 Baolin Wang 2017-04-19 375 disable_runtime:
b0c69b4bace370 Baolin Wang 2017-04-19 376 pm_runtime_put_noidle(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 377 pm_runtime_disable(&pdev->dev);
b0c69b4bace370 Baolin Wang 2017-04-19 378
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 379 return ret;
3429e91a661e1f Sebastian Andrzej Siewior 2012-03-13 380 }
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki