diff options
author | Andy Hu <andy.hu@starfivetech.com> | 2024-03-22 15:35:42 +0300 |
---|---|---|
committer | Andy Hu <andy.hu@starfivetech.com> | 2024-03-22 15:35:42 +0300 |
commit | 077cc6db6398aef2a574c039453b6afb1a1c899c (patch) | |
tree | 3dcd86c4399aef64764c47587894718f0ccb2c77 | |
parent | da1582cf662e0ea7d206d72fc024a0f75abb34b8 (diff) | |
parent | 191e5e2591166868c716c4f81a82ca23f542cd7a (diff) | |
download | linux-077cc6db6398aef2a574c039453b6afb1a1c899c.tar.xz |
Merge tag 'JH7110_SDK_6.6_v1.1.0' into vf2-6.6.y-devel
-rw-r--r-- | arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso | 7 | ||||
-rw-r--r-- | arch/riscv/boot/dts/starfive/jh7110-evb.dts | 1 | ||||
-rw-r--r-- | arch/riscv/boot/dts/starfive/jh7110-evb.dtsi | 3 | ||||
-rw-r--r-- | drivers/media/platform/starfive/v4l2_driver/stfcamss.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-pl022.c | 271 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 69 | ||||
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 57 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 11 |
11 files changed, 431 insertions, 6 deletions
diff --git a/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso index a49866ff93c3..638d13c606e0 100644 --- a/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso +++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso @@ -9,6 +9,7 @@ fragment@0 { target-path = "/soc/spi@10060000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; @@ -17,6 +18,7 @@ fragment@1 { target-path = "/soc/spi@10070000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; @@ -25,6 +27,7 @@ fragment@2 { target-path = "/soc/spi@10080000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; @@ -33,6 +36,7 @@ fragment@3 { target-path = "/soc/spi@12070000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; @@ -41,6 +45,7 @@ fragment@4 { target-path = "/soc/spi@12080000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; @@ -49,6 +54,7 @@ fragment@5 { target-path = "/soc/spi@12090000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; @@ -57,6 +63,7 @@ fragment@6 { target-path = "/soc/spi@120a0000"; __overlay__ { + compatible = "starfive,jh7110-spi-pl022"; status = "okay"; }; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110-evb.dts b/arch/riscv/boot/dts/starfive/jh7110-evb.dts index 3e9e1372f510..fb34043e2ba4 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-evb.dts +++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts @@ -31,5 +31,6 @@ }; &usb0 { + xhci-lowmem-pool; status = "okay"; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi index 1c6ebe473353..8d1ad85f5a30 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi @@ -568,6 +568,9 @@ spl@0 { reg = <0x0 0x40000>; }; + uboot-env@f0000 { + reg = <0xf0000 0x10000>; + }; uboot@100000 { reg = <0x100000 0x300000>; }; diff --git a/drivers/media/platform/starfive/v4l2_driver/stfcamss.c b/drivers/media/platform/starfive/v4l2_driver/stfcamss.c index 431801e7a6ed..24bfe8da9c9a 100644 --- a/drivers/media/platform/starfive/v4l2_driver/stfcamss.c +++ b/drivers/media/platform/starfive/v4l2_driver/stfcamss.c @@ -1362,7 +1362,7 @@ static void __exit stfcamss_cleanup(void) platform_driver_unregister(&stfcamss_driver); } -module_init(stfcamss_init); +late_initcall(stfcamss_init); //fs_initcall(stfcamss_init); module_exit(stfcamss_cleanup); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 869f0095e6ac..1420c40fa815 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -34,6 +34,9 @@ #include <linux/of.h> #include <linux/pinctrl/consumer.h> #include <linux/reset.h> +#include <linux/platform_device.h> +#include <linux/clk/clk-conf.h> +#include <linux/pm_domain.h> /* * This macro is used to define some register default values. @@ -2088,7 +2091,10 @@ pl022_platform_data_dt_get(struct device *dev) return NULL; } - pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL); + if (strncmp(dev->bus->name, "platform", strlen("platform"))) + pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL); + else + pd = kzalloc(sizeof(struct pl022_ssp_controller), GFP_KERNEL); if (!pd) return NULL; @@ -2100,6 +2106,172 @@ pl022_platform_data_dt_get(struct device *dev) return pd; } +static int pl022_platform_probe(struct platform_device *pdev, const struct amba_id *id) +{ + struct device *dev = &pdev->dev; + struct spi_controller *host; + struct pl022_ssp_controller *platform_info; + struct amba_device *adev; + struct pl022 *pl022 = NULL; + struct resource *res; + int status = 0; + int irq; + + dev_info(dev, + "ARM PL022 driver for StarFive SoC platform, device ID: 0x%08x\n", + id->id); + + adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL); + adev->dev = pdev->dev; + platform_info = pl022_platform_data_dt_get(dev); + if (!platform_info) { + dev_err(dev, "probe: no platform data defined\n"); + return -ENODEV; + } + /* Allocate host with space for data */ + host = spi_alloc_host(dev, sizeof(struct pl022)); + if (host == NULL) { + dev_err(dev, "probe - cannot alloc SPI host\n"); + return -ENOMEM; + } + + pl022 = spi_controller_get_devdata(host); + pl022->host = host; + pl022->host_info = platform_info; + pl022->adev = adev; + pl022->vendor = id->data; + pl022->host->dev.parent = &pdev->dev; + /* + * Bus Number Which has been Assigned to this SSP controller + * on this board + */ + host->bus_num = platform_info->bus_id; + host->cleanup = pl022_cleanup; + host->setup = pl022_setup; + /* If open CONFIG_PM, auto_runtime_pm should be false when of-platform.*/ + host->auto_runtime_pm = true; + host->transfer_one_message = pl022_transfer_one_message; + host->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; + host->rt = platform_info->rt; + host->dev.of_node = dev->of_node; + host->use_gpio_descriptors = true; + + /* + * Supports mode 0-3, loopback, and active low CS. Transfers are + * always MS bit first on the original pl022. + */ + host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + if (pl022->vendor->extended_cr) + host->mode_bits |= SPI_LSB_FIRST; + + dev_dbg(dev, "BUSNO: %d\n", host->bus_num); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pl022->phybase = res->start; + pl022->virtbase = devm_ioremap_resource(dev, res); + if (pl022->virtbase == NULL) { + status = -ENOMEM; + goto err_no_ioremap; + } + dev_info(dev, "mapped registers from %llx to %llx\n", + pdev->resource->start, pdev->resource->end); + + pl022->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pl022->clk)) { + status = PTR_ERR(pl022->clk); + dev_err(dev, "could not retrieve SSP/SPI bus clock\n"); + goto err_no_clk; + } + status = clk_prepare_enable(pl022->clk); + if (status) { + dev_err(dev, "could not enable SSP/SPI bus clock\n"); + goto err_no_clk_en; + } + + pl022->rst = devm_reset_control_get_exclusive(dev, NULL); + if (!IS_ERR(pl022->rst)) { + status = reset_control_deassert(pl022->rst); + if (status) { + dev_err(dev, "could not deassert SSP/SPI bus reset\n"); + goto err_no_rst_clr; + } + } else { + status = PTR_ERR(pl022->rst); + dev_err(dev, "could not retrieve SSP/SPI bus reset\n"); + goto err_no_rst; + } + + /* Initialize transfer pump */ + tasklet_init(&pl022->pump_transfers, pump_transfers, + (unsigned long)pl022); + + /* Disable SSP */ + writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), + SSP_CR1(pl022->virtbase)); + load_ssp_default_config(pl022); + + /* Obtain IRQ line. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + status = -ENXIO; + goto err_no_irq; + } + status = devm_request_irq(dev, irq, pl022_interrupt_handler, + 0, "pl022", pl022); + if (status < 0) { + dev_err(dev, "probe - cannot get IRQ (%d)\n", status); + goto err_no_irq; + } + + /* Get DMA channels, try autoconfiguration first */ + status = pl022_dma_autoprobe(pl022); + if (status == -EPROBE_DEFER) { + dev_dbg(dev, "deferring probe to get DMA channel\n"); + goto err_no_irq; + } + + /* dma is not used unless configured in the device tree */ + platform_info->enable_dma = 0; + + /* If that failed, use channels from platform_info */ + if (status == 0) + platform_info->enable_dma = 1; + else if (platform_info->enable_dma) { + status = pl022_dma_probe(pl022); + if (status != 0) + platform_info->enable_dma = 0; + } + + /* Register with the SPI framework */ + dev_set_drvdata(dev, pl022); + + status = devm_spi_register_controller(dev, host); + if (status != 0) { + dev_err(dev, + "probe - problem registering spi host\n"); + goto err_spi_register; + } + dev_dbg(dev, "probe succeeded\n"); + + clk_disable_unprepare(pl022->clk); + + return 0; + err_spi_register: + if (platform_info->enable_dma) + pl022_dma_remove(pl022); + err_no_irq: + reset_control_assert(pl022->rst); + err_no_rst_clr: + err_no_rst: + clk_disable_unprepare(pl022->clk); + err_no_clk_en: + err_no_clk: + err_no_ioremap: + release_mem_region(pdev->resource->start, resource_size(pdev->resource)); + spi_controller_put(host); + return status; +} + static int pl022_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; @@ -2230,6 +2402,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) /* Register with the SPI framework */ amba_set_drvdata(adev, pl022); + status = devm_spi_register_controller(&adev->dev, host); if (status != 0) { dev_err_probe(&adev->dev, status, @@ -2238,6 +2411,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) } dev_dbg(dev, "probe succeeded\n"); + platform_info->autosuspend_delay = 100; /* let runtime pm put suspend */ if (platform_info->autosuspend_delay > 0) { dev_info(&adev->dev, @@ -2247,6 +2421,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) platform_info->autosuspend_delay); pm_runtime_use_autosuspend(dev); } + pm_runtime_put(dev); return 0; @@ -2255,6 +2430,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) if (platform_info->enable_dma) pl022_dma_remove(pl022); err_no_irq: + reset_control_assert(pl022->rst); err_no_rst_de: err_no_rst: clk_disable_unprepare(pl022->clk); @@ -2264,6 +2440,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) amba_release_regions(adev); err_no_ioregion: spi_controller_put(host); + return status; } @@ -2456,7 +2633,11 @@ static int __init pl022_init(void) { return amba_driver_register(&pl022_driver); } +#if !IS_MODULE(CONFIG_SPI_PL022) subsys_initcall(pl022_init); +#else +module_init(pl022_init); +#endif static void __exit pl022_exit(void) { @@ -2464,6 +2645,94 @@ static void __exit pl022_exit(void) } module_exit(pl022_exit); +/* + * Register PL022 in platform bus to accommodate overlay use. + * Because overlay only trigger response from the platform bus + * not amba bus. + */ +static int starfive_of_pl022_probe(struct platform_device *pdev) +{ + int ret; + const struct amba_id id = { + .id = 0x00041022, + .mask = 0x000fffff, + .data = &vendor_arm + }; + struct device *dev = &pdev->dev; + + ret = of_clk_set_defaults(dev->of_node, false); + if (ret < 0) + goto err_probe; + + ret = dev_pm_domain_attach(dev, true); + if (ret) + goto err_probe; + + ret = pl022_platform_probe(pdev, &id); + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + + if (ret) { + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + dev_pm_domain_detach(dev, true); + } + +err_probe: + return ret; +} + +static int starfive_of_pl022_remove(struct platform_device *pdev) +{ + struct pl022 *pl022 = dev_get_drvdata(&pdev->dev); + + if (!pl022) + return 0; + + pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + load_ssp_default_config(pl022); + if (pl022->host_info->enable_dma) + pl022_dma_remove(pl022); + + clk_disable_unprepare(pl022->clk); + tasklet_disable(&pl022->pump_transfers); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + dev_pm_domain_detach(&pdev->dev, true); + + return 0; +} + +static const struct of_device_id starfive_of_pl022_match[] = { + { .compatible = "starfive,jh7110-spi-pl022" }, + { }, +}; +MODULE_DEVICE_TABLE(of, starfive_of_pl022_match); + +static struct platform_driver starfive_of_pl022_driver = { + .driver = { + .name = "starfive-spi-pl022", + .of_match_table = starfive_of_pl022_match, + .pm = &pl022_dev_pm_ops, + }, + .probe = starfive_of_pl022_probe, + .remove = starfive_of_pl022_remove, +}; + +#if !IS_MODULE(CONFIG_SPI_PL022) +module_platform_driver(starfive_of_pl022_driver); +#endif +/* platform register end */ + +MODULE_AUTHOR("xingyu.wu <xingyu.wu@starfivetech.com>"); MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); MODULE_DESCRIPTION("PL022 SSP Controller Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 12b6dfeaf658..226e9b248c23 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1439,7 +1439,9 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (ret == 0) urb->transfer_flags |= URB_MAP_LOCAL; } else if (hcd_uses_dma(hcd)) { - if (urb->num_sgs) { + if (urb->transfer_flags & URB_MAP_LOCAL) + return ret; + else if (urb->num_sgs) { int n; /* We don't support sg for isoc transfers ! */ diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 0a37f0d511cf..9b670e913eaa 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/dmapool.h> #include <linux/dma-mapping.h> +#include <linux/genalloc.h> #include "xhci.h" #include "xhci-trace.h" @@ -1840,6 +1841,7 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_lowmem_pool *pool; int i, j, num_ports; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1885,6 +1887,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed medium stream array pool"); + if (xhci->lowmem_pool.pool) { + pool = &xhci->lowmem_pool; + dma_free_noncoherent(dev, pool->size, (void *)pool->cached_base, + pool->dma_addr, DMA_BIDIRECTIONAL); + gen_pool_destroy(pool->pool); + pool->pool = NULL; + } + if (xhci->dcbaa) dma_free_coherent(dev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); @@ -2295,6 +2305,55 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return 0; } +int xhci_setup_local_lowmem(struct xhci_hcd *xhci, size_t size) +{ + int err; + void *buffer; + dma_addr_t dma_addr; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct xhci_lowmem_pool *pool = &xhci->lowmem_pool; + + if (!pool->pool) { + /* minimal alloc one page */ + pool->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(hcd->self.sysdev)); + if (!pool->pool) + return -ENOMEM; + } + + buffer = dma_alloc_noncoherent(hcd->self.sysdev, size, &dma_addr, + DMA_BIDIRECTIONAL, GFP_ATOMIC); + + if (!buffer) { + err = -ENOMEM; + goto destroy_pool; + } + + /* + * 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(pool->pool, (unsigned long)buffer, + dma_addr, size, dev_to_node(hcd->self.sysdev)); + if (err < 0) { + dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n", + err); + dma_free_noncoherent(hcd->self.sysdev, size, buffer, dma_addr, DMA_BIDIRECTIONAL); + goto destroy_pool; + } + + pool->cached_base = (u64)buffer; + pool->dma_addr = dma_addr; + + return 0; + +destroy_pool: + gen_pool_destroy(pool->pool); + pool->pool = NULL; + return err; +} +EXPORT_SYMBOL_GPL(xhci_setup_local_lowmem); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2302,7 +2361,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; u32 page_size, temp; - int i; + int i, ret; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2431,6 +2490,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; + if (xhci->quirks & XHCI_LOCAL_BUFFER) { + ret = xhci_setup_local_lowmem(xhci, xhci->lowmem_pool.size); + if (ret) { + xhci->quirks &= ~XHCI_LOCAL_BUFFER; + xhci_warn(xhci, "WARN: Can't alloc lowmem pool\n"); + } + } + /* * XXX: Might need to set the Interrupter Moderation Register to * something other than the default (~1ms minimum between interrupts). diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index d68e9abcdc69..c2a5a19ae716 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -253,6 +253,15 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk")) xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK; + if (device_property_read_bool(tmpdev, "xhci-lowmem-pool")) { + xhci->quirks |= XHCI_LOCAL_BUFFER; + ret = device_property_read_u32(tmpdev, "lowmem-pool-size", + &xhci->lowmem_pool.size); + if (ret || xhci->lowmem_pool.size >= 4) + xhci->lowmem_pool.size = 4 << 20; + else + xhci->lowmem_pool.size <<= 20; + } device_property_read_u32(tmpdev, "imod-interval-ns", &xhci->imod_interval); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c410a98ed63c..948e43b935d7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3612,7 +3612,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, full_len = urb->transfer_buffer_length; /* If we have scatter/gather list, we use it. */ - if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) { + if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE) + && !(urb->transfer_flags & URB_MAP_LOCAL)) { num_sgs = urb->num_mapped_sgs; sg = urb->sg; addr = (u64) sg_dma_address(sg); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 132b76fa7ca6..ae9d66e75f43 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -18,6 +18,8 @@ #include <linux/slab.h> #include <linux/dmi.h> #include <linux/dma-mapping.h> +#include <linux/dma-map-ops.h> +#include <linux/genalloc.h> #include "xhci.h" #include "xhci-trace.h" @@ -1273,6 +1275,55 @@ static void xhci_unmap_temp_buf(struct usb_hcd *hcd, struct urb *urb) urb->transfer_buffer = NULL; } +static void xhci_map_urb_local(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + void *buffer; + dma_addr_t dma_handle; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool; + + if (lowmem_pool->pool + && (usb_endpoint_type(&urb->ep->desc) == USB_ENDPOINT_XFER_BULK) + && (urb->transfer_buffer_length > PAGE_SIZE) + && urb->num_sgs && urb->sg && (sg_phys(urb->sg) > 0xffffffff)) { + buffer = gen_pool_dma_alloc(lowmem_pool->pool, + urb->transfer_buffer_length, &dma_handle); + if (buffer) { + urb->transfer_dma = dma_handle; + urb->transfer_buffer = buffer; + urb->transfer_flags |= URB_MAP_LOCAL; + if (usb_urb_dir_out(urb)) + sg_copy_to_buffer(urb->sg, urb->num_sgs, + (void *)buffer, + urb->transfer_buffer_length); + } + } + +} + +static void xhci_unmap_urb_local(struct usb_hcd *hcd, struct urb *urb) +{ + dma_addr_t dma_handle; + u64 cached_buffer; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool; + + if (urb->transfer_flags & URB_MAP_LOCAL) { + dma_handle = urb->transfer_dma; + cached_buffer = lowmem_pool->cached_base + + ((u32)urb->transfer_dma & (lowmem_pool->size - 1)); + if (usb_urb_dir_in(urb)) + sg_copy_from_buffer(urb->sg, urb->num_sgs, + (void *)cached_buffer, urb->transfer_buffer_length); + gen_pool_free(lowmem_pool->pool, (unsigned long)urb->transfer_buffer, + urb->transfer_buffer_length); + urb->transfer_flags &= ~URB_MAP_LOCAL; + urb->transfer_buffer = NULL; + } +} + + /* * Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT), * we'll copy the actual data into the TRB address register. This is limited to @@ -1293,9 +1344,11 @@ static int xhci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (xhci_urb_temp_buffer_required(hcd, urb)) return xhci_map_temp_buffer(hcd, urb); } + xhci_map_urb_local(hcd, urb, mem_flags); return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); } + static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { struct xhci_hcd *xhci; @@ -1308,8 +1361,10 @@ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) if ((xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) && unmap_temp_buf) xhci_unmap_temp_buf(hcd, urb); - else + else { + xhci_unmap_urb_local(hcd, urb); usb_hcd_unmap_urb_for_dma(hcd, urb); + } } /** diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 31088602c070..48e2f817247b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1757,6 +1757,13 @@ struct xhci_hub { u8 min_rev; }; +struct xhci_lowmem_pool { + struct gen_pool *pool; + u64 cached_base; + dma_addr_t dma_addr; + unsigned int size; +}; + /* There is one xhci_hcd structure per controller */ struct xhci_hcd { struct usb_hcd *main_hcd; @@ -1908,6 +1915,8 @@ struct xhci_hcd { #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) #define XHCI_ZHAOXIN_HOST BIT_ULL(46) +#define XHCI_LOCAL_BUFFER BIT_ULL(63) + unsigned int num_active_eps; unsigned int limit_active_eps; struct xhci_port *hw_ports; @@ -1937,6 +1946,8 @@ struct xhci_hcd { struct list_head regset_list; void *dbc; + struct xhci_lowmem_pool lowmem_pool; + /* platform-specific data -- must come last */ unsigned long priv[] __aligned(sizeof(s64)); }; |