![Arnd Bergmann](/assets/img/avatar_default.png)
For xhci-hcd platform device, all the DMA parameters are not configured properly, notably dma ops for dwc3 devices. The idea here is that you pass in the parent of_node along with the child device pointer, so it would behave exactly like the parent already does. The difference is that it also handles all the other attributes besides the mask. sysdev will represent the physical device, as seen from firmware or bus.Splitting the usb_bus->controller field into the Linux-internal device (used for the sysfs hierarchy, for printks and for power management) and a new pointer (used for DMA, DT enumeration and phy lookup) probably covers all that we really need. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Sriram Dash <sriram.dash@nxp.com> Tested-by: Baolin Wang <baolin.wang@linaro.org> Tested-by: Brian Norris <briannorris@chromium.org> Tested-by: Alexander Sverdlin <alexander.sverdlin@nokia.com> Tested-by: Vivek Gautam <vivek.gautam@codeaurora.org> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Peter Chen <peter.chen@nxp.com> Cc: Felipe Balbi <felipe.balbi@linux.intel.com> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Sinjan Kumar <sinjank@codeaurora.org> Cc: David Fisher <david.fisher1@synopsys.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: "Thang Q. Nguyen" <tqnguyen@apm.com> Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Cc: Stephen Boyd <sboyd@codeaurora.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Ming Lei <tom.leiming@gmail.com> Cc: Jon Masters <jcm@redhat.com> Cc: Dann Frazier <dann.frazier@canonical.com> Cc: Peter Chen <peter.chen@nxp.com> Cc: Leo Li <pku.leo@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
174 lines
3.8 KiB
C
174 lines
3.8 KiB
C
/*
|
|
* DMA memory management for framework level HCD code (hc_driver)
|
|
*
|
|
* This implementation plugs in through generic "usb_bus" level methods,
|
|
* and should work with all USB controllers, regardless of bus type.
|
|
*
|
|
* Released under the GPLv2 only.
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/io.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dmapool.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/hcd.h>
|
|
|
|
|
|
/*
|
|
* DMA-Coherent Buffers
|
|
*/
|
|
|
|
/* FIXME tune these based on pool statistics ... */
|
|
static size_t pool_max[HCD_BUFFER_POOLS] = {
|
|
32, 128, 512, 2048,
|
|
};
|
|
|
|
void __init usb_init_pool_max(void)
|
|
{
|
|
/*
|
|
* The pool_max values must never be smaller than
|
|
* ARCH_KMALLOC_MINALIGN.
|
|
*/
|
|
if (ARCH_KMALLOC_MINALIGN <= 32)
|
|
; /* Original value is okay */
|
|
else if (ARCH_KMALLOC_MINALIGN <= 64)
|
|
pool_max[0] = 64;
|
|
else if (ARCH_KMALLOC_MINALIGN <= 128)
|
|
pool_max[0] = 0; /* Don't use this pool */
|
|
else
|
|
BUILD_BUG(); /* We don't allow this */
|
|
}
|
|
|
|
/* SETUP primitives */
|
|
|
|
/**
|
|
* hcd_buffer_create - initialize buffer pools
|
|
* @hcd: the bus whose buffer pools are to be initialized
|
|
* Context: !in_interrupt()
|
|
*
|
|
* Call this as part of initializing a host controller that uses the dma
|
|
* memory allocators. It initializes some pools of dma-coherent memory that
|
|
* will be shared by all drivers using that controller.
|
|
*
|
|
* Call hcd_buffer_destroy() to clean up after using those pools.
|
|
*
|
|
* Return: 0 if successful. A negative errno value otherwise.
|
|
*/
|
|
int hcd_buffer_create(struct usb_hcd *hcd)
|
|
{
|
|
char name[16];
|
|
int i, size;
|
|
|
|
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
|
(!is_device_dma_capable(hcd->self.sysdev) &&
|
|
!(hcd->driver->flags & HCD_LOCAL_MEM)))
|
|
return 0;
|
|
|
|
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
|
size = pool_max[i];
|
|
if (!size)
|
|
continue;
|
|
snprintf(name, sizeof(name), "buffer-%d", size);
|
|
hcd->pool[i] = dma_pool_create(name, hcd->self.sysdev,
|
|
size, size, 0);
|
|
if (!hcd->pool[i]) {
|
|
hcd_buffer_destroy(hcd);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* hcd_buffer_destroy - deallocate buffer pools
|
|
* @hcd: the bus whose buffer pools are to be destroyed
|
|
* Context: !in_interrupt()
|
|
*
|
|
* This frees the buffer pools created by hcd_buffer_create().
|
|
*/
|
|
void hcd_buffer_destroy(struct usb_hcd *hcd)
|
|
{
|
|
int i;
|
|
|
|
if (!IS_ENABLED(CONFIG_HAS_DMA))
|
|
return;
|
|
|
|
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
|
struct dma_pool *pool = hcd->pool[i];
|
|
|
|
if (pool) {
|
|
dma_pool_destroy(pool);
|
|
hcd->pool[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* sometimes alloc/free could use kmalloc with GFP_DMA, for
|
|
* better sharing and to leverage mm/slab.c intelligence.
|
|
*/
|
|
|
|
void *hcd_buffer_alloc(
|
|
struct usb_bus *bus,
|
|
size_t size,
|
|
gfp_t mem_flags,
|
|
dma_addr_t *dma
|
|
)
|
|
{
|
|
struct usb_hcd *hcd = bus_to_hcd(bus);
|
|
int i;
|
|
|
|
if (size == 0)
|
|
return NULL;
|
|
|
|
/* some USB hosts just use PIO */
|
|
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
|
(!is_device_dma_capable(bus->sysdev) &&
|
|
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
|
|
*dma = ~(dma_addr_t) 0;
|
|
return kmalloc(size, mem_flags);
|
|
}
|
|
|
|
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
|
if (size <= pool_max[i])
|
|
return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
|
|
}
|
|
return dma_alloc_coherent(hcd->self.sysdev, size, dma, mem_flags);
|
|
}
|
|
|
|
void hcd_buffer_free(
|
|
struct usb_bus *bus,
|
|
size_t size,
|
|
void *addr,
|
|
dma_addr_t dma
|
|
)
|
|
{
|
|
struct usb_hcd *hcd = bus_to_hcd(bus);
|
|
int i;
|
|
|
|
if (!addr)
|
|
return;
|
|
|
|
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
|
(!is_device_dma_capable(bus->sysdev) &&
|
|
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
|
|
kfree(addr);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
|
if (size <= pool_max[i]) {
|
|
dma_pool_free(hcd->pool[i], addr, dma);
|
|
return;
|
|
}
|
|
}
|
|
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
|
|
}
|