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 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;
+
+ list_for_each_entry(mem_tmp, &dev->dma_mems, node) {
+ retval = __dma_release_from_coherent(mem_tmp, order, vaddr);
+ if (retval == 1)
+ break;
+ }
- return __dma_release_from_coherent(mem, order, vaddr);
+ 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..79e743aab62e 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -149,7 +149,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 +194,20 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
xhci->allow_single_roothub = 1;
+ count = of_property_count_elems_of_size(sysdev->of_node, "memory-region",
+ sizeof(u32));
+
+ 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 Fri, Mar 08, 2024 at 09:53:19AM +0000, Howard Yen wrote:
> 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.
..
> +#endif
> +#ifdef CONFIG_DMA_DECLARE_COHERENT
> + INIT_LIST_HEAD(&dev->dma_mems);
> #endif
Side note: Have you used --histogram diff algo? If no, use it in the future.
--
With Best Regards,
Andy Shevchenko
On Fri, Mar 08, 2024 at 09:53:20AM +0000, 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.
..
> + count = of_property_count_elems_of_size(sysdev->of_node, "memory-region",
> + sizeof(u32));
Open coded of_property_count_u32_elems().
--
With Best Regards,
Andy Shevchenko
Hi Howard,
kernel test robot noticed the following build errors:
[auto build test ERROR on usb/usb-testing]
[also build test ERROR on usb/usb-next usb/usb-linus char-misc/char-misc-testing char-misc/char-misc-next char-misc/char-misc-linus driver-core/driver-core-testing driver-core/driver-core-next driver-core/driver-core-linus linus/master v6.8-rc7 next-20240308]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
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/20240308-175649
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/20240308095320.1961469-3-howardyen%40google.com
patch subject: [PATCH v4 2/2] usb: host: xhci-plat: add support for multi memory regions
config: riscv-defconfig (https://download.01.org/0day-ci/archive/20240310/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 503c55e17037436dcd45ac69dea8967e67e3f5e8)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240310/[email protected]/reproduce)
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]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
In file included from drivers/usb/host/xhci-plat.c:12:
In file included from include/linux/dma-mapping.h:11:
In file included from include/linux/scatterlist.h:8:
In file included from include/linux/mm.h:2188:
include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
522 | return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
| ~~~~~~~~~~~ ^ ~~~
>> drivers/usb/host/xhci-plat.c:201:9: error: call to undeclared function 'of_reserved_mem_device_init_by_idx'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
201 | ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
| ^
>> drivers/usb/host/xhci-plat.c:205:5: error: call to undeclared function 'of_reserved_mem_device_release'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
205 | of_reserved_mem_device_release(sysdev);
| ^
drivers/usb/host/xhci-plat.c:169:42: warning: shift count >= width of type [-Wshift-count-overflow]
169 | ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
| ^~~~~~~~~~~~~~~~
include/linux/dma-mapping.h:77:54: note: expanded from macro 'DMA_BIT_MASK'
77 | #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
| ^ ~~~
drivers/usb/host/xhci-plat.c:409:46: warning: shift count >= width of type [-Wshift-count-overflow]
409 | ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
| ^~~~~~~~~~~~~~~~
include/linux/dma-mapping.h:77:54: note: expanded from macro 'DMA_BIT_MASK'
77 | #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
| ^ ~~~
drivers/usb/host/xhci-plat.c:449:2: error: call to undeclared function 'of_reserved_mem_device_release'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
449 | of_reserved_mem_device_release(hcd->self.sysdev);
| ^
3 warnings and 3 errors generated.
vim +/of_reserved_mem_device_init_by_idx +201 drivers/usb/host/xhci-plat.c
> 12 #include <linux/dma-mapping.h>
13 #include <linux/module.h>
14 #include <linux/pci.h>
15 #include <linux/of.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/usb/phy.h>
19 #include <linux/slab.h>
20 #include <linux/acpi.h>
21 #include <linux/usb/of.h>
22 #include <linux/reset.h>
23
24 #include "xhci.h"
25 #include "xhci-plat.h"
26 #include "xhci-mvebu.h"
27
28 static struct hc_driver __read_mostly xhci_plat_hc_driver;
29
30 static int xhci_plat_setup(struct usb_hcd *hcd);
31 static int xhci_plat_start(struct usb_hcd *hcd);
32
33 static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
34 .extra_priv_size = sizeof(struct xhci_plat_priv),
35 .reset = xhci_plat_setup,
36 .start = xhci_plat_start,
37 };
38
39 static void xhci_priv_plat_start(struct usb_hcd *hcd)
40 {
41 struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
42
43 if (priv->plat_start)
44 priv->plat_start(hcd);
45 }
46
47 static int xhci_priv_init_quirk(struct usb_hcd *hcd)
48 {
49 struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
50
51 if (!priv->init_quirk)
52 return 0;
53
54 return priv->init_quirk(hcd);
55 }
56
57 static int xhci_priv_suspend_quirk(struct usb_hcd *hcd)
58 {
59 struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
60
61 if (!priv->suspend_quirk)
62 return 0;
63
64 return priv->suspend_quirk(hcd);
65 }
66
67 static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
68 {
69 struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
70
71 if (!priv->resume_quirk)
72 return 0;
73
74 return priv->resume_quirk(hcd);
75 }
76
77 static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
78 {
79 struct xhci_plat_priv *priv = xhci_to_priv(xhci);
80
81 xhci->quirks |= priv->quirks;
82 }
83
84 /* called during probe() after chip reset completes */
85 static int xhci_plat_setup(struct usb_hcd *hcd)
86 {
87 int ret;
88
89
90 ret = xhci_priv_init_quirk(hcd);
91 if (ret)
92 return ret;
93
94 return xhci_gen_setup(hcd, xhci_plat_quirks);
95 }
96
97 static int xhci_plat_start(struct usb_hcd *hcd)
98 {
99 xhci_priv_plat_start(hcd);
100 return xhci_run(hcd);
101 }
102
103 #ifdef CONFIG_OF
104 static const struct xhci_plat_priv xhci_plat_marvell_armada = {
105 .init_quirk = xhci_mvebu_mbus_init_quirk,
106 };
107
108 static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
109 .init_quirk = xhci_mvebu_a3700_init_quirk,
110 };
111
112 static const struct xhci_plat_priv xhci_plat_brcm = {
113 .quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS,
114 };
115
116 static const struct of_device_id usb_xhci_of_match[] = {
117 {
118 .compatible = "generic-xhci",
119 }, {
120 .compatible = "xhci-platform",
121 }, {
122 .compatible = "marvell,armada-375-xhci",
123 .data = &xhci_plat_marvell_armada,
124 }, {
125 .compatible = "marvell,armada-380-xhci",
126 .data = &xhci_plat_marvell_armada,
127 }, {
128 .compatible = "marvell,armada3700-xhci",
129 .data = &xhci_plat_marvell_armada3700,
130 }, {
131 .compatible = "brcm,xhci-brcm-v2",
132 .data = &xhci_plat_brcm,
133 }, {
134 .compatible = "brcm,bcm2711-xhci",
135 .data = &xhci_plat_brcm,
136 }, {
137 .compatible = "brcm,bcm7445-xhci",
138 .data = &xhci_plat_brcm,
139 },
140 {},
141 };
142 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
143 #endif
144
145 int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match)
146 {
147 const struct hc_driver *driver;
148 struct device *tmpdev;
149 struct xhci_hcd *xhci;
150 struct resource *res;
151 struct usb_hcd *hcd, *usb3_hcd;
152 int i, count, ret;
153 int irq;
154 struct xhci_plat_priv *priv = NULL;
155 bool of_match;
156
157 if (usb_disabled())
158 return -ENODEV;
159
160 driver = &xhci_plat_hc_driver;
161
162 irq = platform_get_irq(pdev, 0);
163 if (irq < 0)
164 return irq;
165
166 if (!sysdev)
167 sysdev = &pdev->dev;
168
169 ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
170 if (ret)
171 return ret;
172
173 pm_runtime_set_active(&pdev->dev);
174 pm_runtime_enable(&pdev->dev);
175 pm_runtime_get_noresume(&pdev->dev);
176
177 hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
178 dev_name(&pdev->dev), NULL);
179 if (!hcd) {
180 ret = -ENOMEM;
181 goto disable_runtime;
182 }
183
184 hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
185 if (IS_ERR(hcd->regs)) {
186 ret = PTR_ERR(hcd->regs);
187 goto put_hcd;
188 }
189
190 hcd->rsrc_start = res->start;
191 hcd->rsrc_len = resource_size(res);
192
193 xhci = hcd_to_xhci(hcd);
194
195 xhci->allow_single_roothub = 1;
196
197 count = of_property_count_elems_of_size(sysdev->of_node, "memory-region",
198 sizeof(u32));
199
200 for (i = 0; i < count; i++) {
> 201 ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
202 if (ret) {
203 dev_err(sysdev, "Could not get reserved memory\n");
204 if (i > 0)
> 205 of_reserved_mem_device_release(sysdev);
206
207 return ret;
208 }
209 }
210
211 /*
212 * Not all platforms have clks so it is not an error if the
213 * clock do not exist.
214 */
215 xhci->reg_clk = devm_clk_get_optional(&pdev->dev, "reg");
216 if (IS_ERR(xhci->reg_clk)) {
217 ret = PTR_ERR(xhci->reg_clk);
218 goto put_hcd;
219 }
220
221 xhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
222 if (IS_ERR(xhci->clk)) {
223 ret = PTR_ERR(xhci->clk);
224 goto put_hcd;
225 }
226
227 xhci->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
228 if (IS_ERR(xhci->reset)) {
229 ret = PTR_ERR(xhci->reset);
230 goto put_hcd;
231 }
232
233 ret = reset_control_deassert(xhci->reset);
234 if (ret)
235 goto put_hcd;
236
237 ret = clk_prepare_enable(xhci->reg_clk);
238 if (ret)
239 goto err_reset;
240
241 ret = clk_prepare_enable(xhci->clk);
242 if (ret)
243 goto disable_reg_clk;
244
245 if (priv_match) {
246 priv = hcd_to_xhci_priv(hcd);
247 /* Just copy data for now */
248 *priv = *priv_match;
249 }
250
251 device_set_wakeup_capable(&pdev->dev, true);
252
253 xhci->main_hcd = hcd;
254
255 /* imod_interval is the interrupt moderation value in nanoseconds. */
256 xhci->imod_interval = 40000;
257
258 /* Iterate over all parent nodes for finding quirks */
259 for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
260
261 if (device_property_read_bool(tmpdev, "usb2-lpm-disable"))
262 xhci->quirks |= XHCI_HW_LPM_DISABLE;
263
264 if (device_property_read_bool(tmpdev, "usb3-lpm-capable"))
265 xhci->quirks |= XHCI_LPM_SUPPORT;
266
267 if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
268 xhci->quirks |= XHCI_BROKEN_PORT_PED;
269
270 if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
271 xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
272
273 device_property_read_u32(tmpdev, "imod-interval-ns",
274 &xhci->imod_interval);
275 }
276
277 /*
278 * Drivers such as dwc3 manages PHYs themself (and rely on driver name
279 * matching for the xhci platform device).
280 */
281 of_match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
282 if (of_match) {
283 hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
284 if (IS_ERR(hcd->usb_phy)) {
285 ret = PTR_ERR(hcd->usb_phy);
286 if (ret == -EPROBE_DEFER)
287 goto disable_clk;
288 hcd->usb_phy = NULL;
289 } else {
290 ret = usb_phy_init(hcd->usb_phy);
291 if (ret)
292 goto disable_clk;
293 }
294 }
295
296 hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
297
298 if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
299 hcd->skip_phy_initialization = 1;
300
301 if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
302 xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
303
304 ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
305 if (ret)
306 goto disable_usb_phy;
307
308 if (!xhci_has_one_roothub(xhci)) {
309 xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
310 dev_name(&pdev->dev), hcd);
311 if (!xhci->shared_hcd) {
312 ret = -ENOMEM;
313 goto dealloc_usb2_hcd;
314 }
315
316 if (of_match) {
317 xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev,
318 "usb-phy", 1);
319 if (IS_ERR(xhci->shared_hcd->usb_phy)) {
320 xhci->shared_hcd->usb_phy = NULL;
321 } else {
322 ret = usb_phy_init(xhci->shared_hcd->usb_phy);
323 if (ret)
324 dev_err(sysdev, "%s init usb3phy fail (ret=%d)\n",
325 __func__, ret);
326 }
327 }
328
329 xhci->shared_hcd->tpl_support = hcd->tpl_support;
330 }
331
332 usb3_hcd = xhci_get_usb3_hcd(xhci);
333 if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4)
334 usb3_hcd->can_do_streams = 1;
335
336 if (xhci->shared_hcd) {
337 ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
338 if (ret)
339 goto put_usb3_hcd;
340 }
341
342 device_enable_async_suspend(&pdev->dev);
343 pm_runtime_put_noidle(&pdev->dev);
344
345 /*
346 * Prevent runtime pm from being on as default, users should enable
347 * runtime pm using power/control in sysfs.
348 */
349 pm_runtime_forbid(&pdev->dev);
350
351 return 0;
352
353
354 put_usb3_hcd:
355 usb_put_hcd(xhci->shared_hcd);
356
357 dealloc_usb2_hcd:
358 usb_remove_hcd(hcd);
359
360 disable_usb_phy:
361 usb_phy_shutdown(hcd->usb_phy);
362
363 disable_clk:
364 clk_disable_unprepare(xhci->clk);
365
366 disable_reg_clk:
367 clk_disable_unprepare(xhci->reg_clk);
368
369 err_reset:
370 reset_control_assert(xhci->reset);
371
372 put_hcd:
373 usb_put_hcd(hcd);
374
375 disable_runtime:
376 pm_runtime_put_noidle(&pdev->dev);
377 pm_runtime_disable(&pdev->dev);
378
379 return ret;
380 }
381 EXPORT_SYMBOL_GPL(xhci_plat_probe);
382
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Howard,
kernel test robot noticed the following build errors:
[auto build test ERROR on usb/usb-testing]
[also build test ERROR on usb/usb-next usb/usb-linus char-misc/char-misc-testing char-misc/char-misc-next char-misc/char-misc-linus driver-core/driver-core-testing driver-core/driver-core-next driver-core/driver-core-linus linus/master v6.8-rc7 next-20240308]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
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/20240308-175649
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/20240308095320.1961469-3-howardyen%40google.com
patch subject: [PATCH v4 2/2] usb: host: xhci-plat: add support for multi memory regions
config: arm64-defconfig (https://download.01.org/0day-ci/archive/20240310/[email protected]/config)
compiler: aarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240310/[email protected]/reproduce)
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]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
drivers/usb/host/xhci-plat.c: In function 'xhci_plat_probe':
>> drivers/usb/host/xhci-plat.c:201:23: error: implicit declaration of function 'of_reserved_mem_device_init_by_idx' [-Werror=implicit-function-declaration]
201 | ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/usb/host/xhci-plat.c:205:33: error: implicit declaration of function 'of_reserved_mem_device_release' [-Werror=implicit-function-declaration]
205 | of_reserved_mem_device_release(sysdev);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +/of_reserved_mem_device_init_by_idx +201 drivers/usb/host/xhci-plat.c
144
145 int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match)
146 {
147 const struct hc_driver *driver;
148 struct device *tmpdev;
149 struct xhci_hcd *xhci;
150 struct resource *res;
151 struct usb_hcd *hcd, *usb3_hcd;
152 int i, count, ret;
153 int irq;
154 struct xhci_plat_priv *priv = NULL;
155 bool of_match;
156
157 if (usb_disabled())
158 return -ENODEV;
159
160 driver = &xhci_plat_hc_driver;
161
162 irq = platform_get_irq(pdev, 0);
163 if (irq < 0)
164 return irq;
165
166 if (!sysdev)
167 sysdev = &pdev->dev;
168
169 ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
170 if (ret)
171 return ret;
172
173 pm_runtime_set_active(&pdev->dev);
174 pm_runtime_enable(&pdev->dev);
175 pm_runtime_get_noresume(&pdev->dev);
176
177 hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
178 dev_name(&pdev->dev), NULL);
179 if (!hcd) {
180 ret = -ENOMEM;
181 goto disable_runtime;
182 }
183
184 hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
185 if (IS_ERR(hcd->regs)) {
186 ret = PTR_ERR(hcd->regs);
187 goto put_hcd;
188 }
189
190 hcd->rsrc_start = res->start;
191 hcd->rsrc_len = resource_size(res);
192
193 xhci = hcd_to_xhci(hcd);
194
195 xhci->allow_single_roothub = 1;
196
197 count = of_property_count_elems_of_size(sysdev->of_node, "memory-region",
198 sizeof(u32));
199
200 for (i = 0; i < count; i++) {
> 201 ret = of_reserved_mem_device_init_by_idx(sysdev, sysdev->of_node, i);
202 if (ret) {
203 dev_err(sysdev, "Could not get reserved memory\n");
204 if (i > 0)
> 205 of_reserved_mem_device_release(sysdev);
206
207 return ret;
208 }
209 }
210
211 /*
212 * Not all platforms have clks so it is not an error if the
213 * clock do not exist.
214 */
215 xhci->reg_clk = devm_clk_get_optional(&pdev->dev, "reg");
216 if (IS_ERR(xhci->reg_clk)) {
217 ret = PTR_ERR(xhci->reg_clk);
218 goto put_hcd;
219 }
220
221 xhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
222 if (IS_ERR(xhci->clk)) {
223 ret = PTR_ERR(xhci->clk);
224 goto put_hcd;
225 }
226
227 xhci->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
228 if (IS_ERR(xhci->reset)) {
229 ret = PTR_ERR(xhci->reset);
230 goto put_hcd;
231 }
232
233 ret = reset_control_deassert(xhci->reset);
234 if (ret)
235 goto put_hcd;
236
237 ret = clk_prepare_enable(xhci->reg_clk);
238 if (ret)
239 goto err_reset;
240
241 ret = clk_prepare_enable(xhci->clk);
242 if (ret)
243 goto disable_reg_clk;
244
245 if (priv_match) {
246 priv = hcd_to_xhci_priv(hcd);
247 /* Just copy data for now */
248 *priv = *priv_match;
249 }
250
251 device_set_wakeup_capable(&pdev->dev, true);
252
253 xhci->main_hcd = hcd;
254
255 /* imod_interval is the interrupt moderation value in nanoseconds. */
256 xhci->imod_interval = 40000;
257
258 /* Iterate over all parent nodes for finding quirks */
259 for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
260
261 if (device_property_read_bool(tmpdev, "usb2-lpm-disable"))
262 xhci->quirks |= XHCI_HW_LPM_DISABLE;
263
264 if (device_property_read_bool(tmpdev, "usb3-lpm-capable"))
265 xhci->quirks |= XHCI_LPM_SUPPORT;
266
267 if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
268 xhci->quirks |= XHCI_BROKEN_PORT_PED;
269
270 if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
271 xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
272
273 device_property_read_u32(tmpdev, "imod-interval-ns",
274 &xhci->imod_interval);
275 }
276
277 /*
278 * Drivers such as dwc3 manages PHYs themself (and rely on driver name
279 * matching for the xhci platform device).
280 */
281 of_match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
282 if (of_match) {
283 hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
284 if (IS_ERR(hcd->usb_phy)) {
285 ret = PTR_ERR(hcd->usb_phy);
286 if (ret == -EPROBE_DEFER)
287 goto disable_clk;
288 hcd->usb_phy = NULL;
289 } else {
290 ret = usb_phy_init(hcd->usb_phy);
291 if (ret)
292 goto disable_clk;
293 }
294 }
295
296 hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
297
298 if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
299 hcd->skip_phy_initialization = 1;
300
301 if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
302 xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
303
304 ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
305 if (ret)
306 goto disable_usb_phy;
307
308 if (!xhci_has_one_roothub(xhci)) {
309 xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
310 dev_name(&pdev->dev), hcd);
311 if (!xhci->shared_hcd) {
312 ret = -ENOMEM;
313 goto dealloc_usb2_hcd;
314 }
315
316 if (of_match) {
317 xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev,
318 "usb-phy", 1);
319 if (IS_ERR(xhci->shared_hcd->usb_phy)) {
320 xhci->shared_hcd->usb_phy = NULL;
321 } else {
322 ret = usb_phy_init(xhci->shared_hcd->usb_phy);
323 if (ret)
324 dev_err(sysdev, "%s init usb3phy fail (ret=%d)\n",
325 __func__, ret);
326 }
327 }
328
329 xhci->shared_hcd->tpl_support = hcd->tpl_support;
330 }
331
332 usb3_hcd = xhci_get_usb3_hcd(xhci);
333 if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4)
334 usb3_hcd->can_do_streams = 1;
335
336 if (xhci->shared_hcd) {
337 ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
338 if (ret)
339 goto put_usb3_hcd;
340 }
341
342 device_enable_async_suspend(&pdev->dev);
343 pm_runtime_put_noidle(&pdev->dev);
344
345 /*
346 * Prevent runtime pm from being on as default, users should enable
347 * runtime pm using power/control in sysfs.
348 */
349 pm_runtime_forbid(&pdev->dev);
350
351 return 0;
352
353
354 put_usb3_hcd:
355 usb_put_hcd(xhci->shared_hcd);
356
357 dealloc_usb2_hcd:
358 usb_remove_hcd(hcd);
359
360 disable_usb_phy:
361 usb_phy_shutdown(hcd->usb_phy);
362
363 disable_clk:
364 clk_disable_unprepare(xhci->clk);
365
366 disable_reg_clk:
367 clk_disable_unprepare(xhci->reg_clk);
368
369 err_reset:
370 reset_control_assert(xhci->reset);
371
372 put_hcd:
373 usb_put_hcd(hcd);
374
375 disable_runtime:
376 pm_runtime_put_noidle(&pdev->dev);
377 pm_runtime_disable(&pdev->dev);
378
379 return ret;
380 }
381 EXPORT_SYMBOL_GPL(xhci_plat_probe);
382
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki