Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0310c2f authored by Laurentiu Tudor's avatar Laurentiu Tudor Committed by Christoph Hellwig
Browse files

USB: use genalloc for USB HCs with local memory



For HCs that have local memory, replace the current DMA API usage with
a genalloc generic allocator to manage the mappings for these devices.
To help users, introduce a new HCD API, usb_hcd_setup_local_mem() that
will setup up the genalloc backing up the device local memory. It will
be used in subsequent patches.  This is in preparation for dropping
the existing "coherent" dma mem declaration APIs.  The current
implementation was relying on a short circuit in the DMA API that in
the end, was acting as an allocator for these type of devices.

Signed-off-by: default avatarLaurentiu Tudor <laurentiu.tudor@nxp.com>
Tested-by: default avatarFredrik Noring <noring@nocrew.org>
Reviewed-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent da83a722
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -45,6 +45,7 @@ config USB_ARCH_HAS_HCD
config USB
config USB
	tristate "Support for Host-side USB"
	tristate "Support for Host-side USB"
	depends on USB_ARCH_HAS_HCD
	depends on USB_ARCH_HAS_HCD
	select GENERIC_ALLOCATOR
	select USB_COMMON
	select USB_COMMON
	select NLS  # for UTF-8 strings
	select NLS  # for UTF-8 strings
	---help---
	---help---
+9 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/dmapool.h>
#include <linux/genalloc.h>
#include <linux/usb.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/hcd.h>


@@ -124,6 +125,9 @@ void *hcd_buffer_alloc(
	if (size == 0)
	if (size == 0)
		return NULL;
		return NULL;


	if (hcd->localmem_pool)
		return gen_pool_dma_alloc(hcd->localmem_pool, size, dma);

	/* some USB hosts just use PIO */
	/* some USB hosts just use PIO */
	if (!IS_ENABLED(CONFIG_HAS_DMA) ||
	if (!IS_ENABLED(CONFIG_HAS_DMA) ||
	    (!is_device_dma_capable(bus->sysdev) &&
	    (!is_device_dma_capable(bus->sysdev) &&
@@ -152,6 +156,11 @@ void hcd_buffer_free(
	if (!addr)
	if (!addr)
		return;
		return;


	if (hcd->localmem_pool) {
		gen_pool_free(hcd->localmem_pool, (unsigned long)addr, size);
		return;
	}

	if (!IS_ENABLED(CONFIG_HAS_DMA) ||
	if (!IS_ENABLED(CONFIG_HAS_DMA) ||
	    (!is_device_dma_capable(bus->sysdev) &&
	    (!is_device_dma_capable(bus->sysdev) &&
	     !(hcd->driver->flags & HCD_LOCAL_MEM))) {
	     !(hcd->driver->flags & HCD_LOCAL_MEM))) {
+36 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,8 @@
#include <linux/workqueue.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/genalloc.h>
#include <linux/io.h>


#include <linux/phy/phy.h>
#include <linux/phy/phy.h>
#include <linux/usb.h>
#include <linux/usb.h>
@@ -3039,6 +3041,40 @@ usb_hcd_platform_shutdown(struct platform_device *dev)
}
}
EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);


int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr,
			    dma_addr_t dma, size_t size)
{
	int err;
	void *local_mem;

	hcd->localmem_pool = devm_gen_pool_create(hcd->self.sysdev, PAGE_SHIFT,
						  dev_to_node(hcd->self.sysdev),
						  dev_name(hcd->self.sysdev));
	if (IS_ERR(hcd->localmem_pool))
		return PTR_ERR(hcd->localmem_pool);

	local_mem = devm_memremap(hcd->self.sysdev, phys_addr,
				  size, MEMREMAP_WC);
	if (!local_mem)
		return -ENOMEM;

	/*
	 * Here we pass a dma_addr_t but the arg type is a phys_addr_t.
	 * It's not backed by system memory and thus there's no kernel mapping
	 * for it.
	 */
	err = gen_pool_add_virt(hcd->localmem_pool, (unsigned long)local_mem,
				dma, size, dev_to_node(hcd->self.sysdev));
	if (err < 0) {
		dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n",
			err);
		return err;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(usb_hcd_setup_local_mem);

/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/


#if IS_ENABLED(CONFIG_USB_MON)
#if IS_ENABLED(CONFIG_USB_MON)
+18 −5
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@
#include <linux/dmapool.h>
#include <linux/dmapool.h>
#include <linux/workqueue.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/debugfs.h>
#include <linux/genalloc.h>


#include <asm/io.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/irq.h>
@@ -505,8 +506,15 @@ static int ohci_init (struct ohci_hcd *ohci)
	timer_setup(&ohci->io_watchdog, io_watchdog_func, 0);
	timer_setup(&ohci->io_watchdog, io_watchdog_func, 0);
	ohci->prev_frame_no = IO_WATCHDOG_OFF;
	ohci->prev_frame_no = IO_WATCHDOG_OFF;


	if (hcd->localmem_pool)
		ohci->hcca = gen_pool_dma_alloc(hcd->localmem_pool,
						sizeof(*ohci->hcca),
						&ohci->hcca_dma);
	else
		ohci->hcca = dma_alloc_coherent(hcd->self.controller,
		ohci->hcca = dma_alloc_coherent(hcd->self.controller,
			sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
						sizeof(*ohci->hcca),
						&ohci->hcca_dma,
						GFP_KERNEL);
	if (!ohci->hcca)
	if (!ohci->hcca)
		return -ENOMEM;
		return -ENOMEM;


@@ -990,8 +998,13 @@ static void ohci_stop (struct usb_hcd *hcd)
	remove_debug_files (ohci);
	remove_debug_files (ohci);
	ohci_mem_cleanup (ohci);
	ohci_mem_cleanup (ohci);
	if (ohci->hcca) {
	if (ohci->hcca) {
		if (hcd->localmem_pool)
			gen_pool_free(hcd->localmem_pool,
				      (unsigned long)ohci->hcca,
				      sizeof(*ohci->hcca));
		else
			dma_free_coherent(hcd->self.controller,
			dma_free_coherent(hcd->self.controller,
				sizeof *ohci->hcca,
					  sizeof(*ohci->hcca),
					  ohci->hcca, ohci->hcca_dma);
					  ohci->hcca, ohci->hcca_dma);
		ohci->hcca = NULL;
		ohci->hcca = NULL;
		ohci->hcca_dma = 0;
		ohci->hcca_dma = 0;
+31 −4
Original line number Original line Diff line number Diff line
@@ -36,6 +36,13 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)


static int ohci_mem_init (struct ohci_hcd *ohci)
static int ohci_mem_init (struct ohci_hcd *ohci)
{
{
	/*
	 * HCs with local memory allocate from localmem_pool so there's
	 * no need to create the below dma pools.
	 */
	if (ohci_to_hcd(ohci)->localmem_pool)
		return 0;

	ohci->td_cache = dma_pool_create ("ohci_td",
	ohci->td_cache = dma_pool_create ("ohci_td",
		ohci_to_hcd(ohci)->self.controller,
		ohci_to_hcd(ohci)->self.controller,
		sizeof (struct td),
		sizeof (struct td),
@@ -84,7 +91,11 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
{
{
	dma_addr_t	dma;
	dma_addr_t	dma;
	struct td	*td;
	struct td	*td;
	struct usb_hcd	*hcd = ohci_to_hcd(hc);


	if (hcd->localmem_pool)
		td = gen_pool_dma_zalloc(hcd->localmem_pool, sizeof(*td), &dma);
	else
		td = dma_pool_zalloc(hc->td_cache, mem_flags, &dma);
		td = dma_pool_zalloc(hc->td_cache, mem_flags, &dma);
	if (td) {
	if (td) {
		/* in case hc fetches it, make it look dead */
		/* in case hc fetches it, make it look dead */
@@ -99,6 +110,7 @@ static void
td_free (struct ohci_hcd *hc, struct td *td)
td_free (struct ohci_hcd *hc, struct td *td)
{
{
	struct td	**prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
	struct td	**prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
	struct usb_hcd	*hcd = ohci_to_hcd(hc);


	while (*prev && *prev != td)
	while (*prev && *prev != td)
		prev = &(*prev)->td_hash;
		prev = &(*prev)->td_hash;
@@ -106,6 +118,11 @@ td_free (struct ohci_hcd *hc, struct td *td)
		*prev = td->td_hash;
		*prev = td->td_hash;
	else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
	else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
		ohci_dbg (hc, "no hash for td %p\n", td);
		ohci_dbg (hc, "no hash for td %p\n", td);

	if (hcd->localmem_pool)
		gen_pool_free(hcd->localmem_pool, (unsigned long)td,
			      sizeof(*td));
	else
		dma_pool_free(hc->td_cache, td, td->td_dma);
		dma_pool_free(hc->td_cache, td, td->td_dma);
}
}


@@ -117,7 +134,11 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
{
{
	dma_addr_t	dma;
	dma_addr_t	dma;
	struct ed	*ed;
	struct ed	*ed;
	struct usb_hcd	*hcd = ohci_to_hcd(hc);


	if (hcd->localmem_pool)
		ed = gen_pool_dma_zalloc(hcd->localmem_pool, sizeof(*ed), &dma);
	else
		ed = dma_pool_zalloc(hc->ed_cache, mem_flags, &dma);
		ed = dma_pool_zalloc(hc->ed_cache, mem_flags, &dma);
	if (ed) {
	if (ed) {
		INIT_LIST_HEAD (&ed->td_list);
		INIT_LIST_HEAD (&ed->td_list);
@@ -129,6 +150,12 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
static void
static void
ed_free (struct ohci_hcd *hc, struct ed *ed)
ed_free (struct ohci_hcd *hc, struct ed *ed)
{
{
	struct usb_hcd	*hcd = ohci_to_hcd(hc);

	if (hcd->localmem_pool)
		gen_pool_free(hcd->localmem_pool, (unsigned long)ed,
			      sizeof(*ed));
	else
		dma_pool_free(hc->ed_cache, ed, ed->dma);
		dma_pool_free(hc->ed_cache, ed, ed->dma);
}
}
Loading