Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932378AbaBEGw3 (ORCPT ); Wed, 5 Feb 2014 01:52:29 -0500 Received: from mail-pa0-f52.google.com ([209.85.220.52]:35774 "EHLO mail-pa0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932249AbaBEGwP (ORCPT ); Wed, 5 Feb 2014 01:52:15 -0500 From: Magnus Damm To: linux-pci@vger.kernel.org Cc: linux-sh@vger.kernel.org, linux-kernel@vger.kernel.org, valentine.barshak@cogentembedded.com, horms@verge.net.au, bhelgaas@google.com, Magnus Damm , ben.dooks@codethink.co.uk Date: Wed, 05 Feb 2014 15:53:25 +0900 Message-Id: <20140205065325.29445.19829.sendpatchset@w520> In-Reply-To: <20140205065243.29445.76593.sendpatchset@w520> References: <20140205065243.29445.76593.sendpatchset@w520> Subject: [PATCH 03/04] PCI: rcar: Add DMABOUNCE support Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Magnus Damm Add DMABOUNCE support together with a local memory area for all PCI devices hanging off this bridge. 4MiB of memory is set aside for coherent allocations which is shared between the on-chip OHCI and EHCI devices that are hanging off the PCI bridge. With this patch the driver will work for all CONFIG_VMSPLIT settings. Signed-off-by: Magnus Damm --- drivers/pci/host/Kconfig | 2 drivers/pci/host/pci-rcar-gen2.c | 262 +++++++++++++++++++++++++++++++++++++- 2 files changed, 261 insertions(+), 3 deletions(-) --- 0001/drivers/pci/host/Kconfig +++ work/drivers/pci/host/Kconfig 2014-02-04 17:23:57.000000000 +0900 @@ -28,6 +28,8 @@ config PCI_TEGRA config PCI_RCAR_GEN2 bool "Renesas R-Car Gen2 Internal PCI controller" depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + select GENERIC_ALLOCATOR + select DMABOUNCE help Say Y here if you want internal PCI support on R-Car Gen2 SoC. There are 3 internal PCI controllers available with a single --- 0006/drivers/pci/host/pci-rcar-gen2.c +++ work/drivers/pci/host/pci-rcar-gen2.c 2014-02-04 17:26:10.000000000 +0900 @@ -15,11 +15,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include /* AHB-PCI Bridge PCI communication registers */ #define RCAR_AHBPCI_PCICOM_OFFSET 0x800 @@ -83,6 +86,11 @@ struct rcar_pci_priv { struct resource *cfg_res; int irq; unsigned long window_size; + u32 window_base; + struct notifier_block bus_notifier; + struct gen_pool *dma_pool; + unsigned long dma_size; + dma_addr_t dma_phys; }; /* PCI configuration space operations */ @@ -217,8 +225,8 @@ static int __init rcar_pci_setup(int nr, RCAR_PCI_ARBITER_PCIBP_MODE; iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); - /* PCI-AHB mapping: 0x40000000 base */ - iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16, + /* PCI-AHB mapping: dynamic base */ + iowrite32(priv->window_base | RCAR_PCIAHB_PREFETCH16, reg + RCAR_PCIAHB_WIN1_CTR_REG); /* AHB-PCI mapping: OHCI/EHCI registers */ @@ -229,7 +237,7 @@ static int __init rcar_pci_setup(int nr, iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG, reg + RCAR_AHBPCI_WIN1_CTR_REG); /* Set PCI-AHB Window1 address */ - iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH, + iowrite32(priv->window_base | PCI_BASE_ADDRESS_MEM_PREFETCH, reg + PCI_BASE_ADDRESS_1); /* Set AHB-PCI bridge PCI communication area address */ val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET; @@ -258,12 +266,231 @@ static struct pci_ops rcar_pci_ops = { .write = rcar_pci_write_config, }; +static struct rcar_pci_priv *rcar_pci_dev_to_priv(struct device *dev) +{ + struct pci_sys_data *sys = to_pci_dev(dev)->sysdata; + + return sys->private_data; +} + +static void *rcar_pci_dma_alloc(struct device *dev, size_t size, + dma_addr_t *handle, gfp_t gfp, + struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return (void *)gen_pool_dma_alloc(p->dma_pool, size, handle); +} + +static void rcar_pci_dma_free(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t handle, struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + gen_pool_free(p->dma_pool, (unsigned long)cpu_addr, size); +} + +static struct dma_map_ops rcar_pci_dma_ops_init = { + .alloc = rcar_pci_dma_alloc, + .free = rcar_pci_dma_free, +}; + +static int rcar_pci_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->mmap(p->dev, vma, cpu_addr, + dma_addr, size, attrs); +} + +static int rcar_pci_dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, + size_t size, struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->get_sgtable(p->dev, sgt, cpu_addr, + handle, size, attrs); +} + +static dma_addr_t rcar_pci_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->map_page(p->dev, page, offset, + size, dir, attrs); +} + +static void rcar_pci_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + get_dma_ops(p->dev)->unmap_page(p->dev, dma_handle, size, dir, attrs); +} + +static int rcar_pci_dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->map_sg(p->dev, sg, nents, dir, attrs); +} + +static void rcar_pci_dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + get_dma_ops(p->dev)->unmap_sg(p->dev, sg, nents, dir, attrs); +} + +static void rcar_pci_dma_sync_single_for_cpu(struct device *dev, + dma_addr_t dma_handle, + size_t size, + enum dma_data_direction dir) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + get_dma_ops(p->dev)->sync_single_for_cpu(p->dev, dma_handle, + size, dir); +} + +static void rcar_pci_dma_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, + size_t size, + enum dma_data_direction dir) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + get_dma_ops(p->dev)->sync_single_for_device(p->dev, dma_handle, + size, dir); +} + +static void rcar_pci_dma_sync_sg_for_cpu(struct device *dev, + struct scatterlist *sg, + int nents, + enum dma_data_direction dir) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + get_dma_ops(p->dev)->sync_sg_for_cpu(p->dev, sg, nents, dir); +} + +static void rcar_pci_dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, + int nents, + enum dma_data_direction dir) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + get_dma_ops(p->dev)->sync_sg_for_device(p->dev, sg, nents, dir); +} + +static int rcar_pci_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->mapping_error(p->dev, dma_addr); +} + +static int rcar_pci_dma_supported(struct device *dev, u64 mask) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->dma_supported(p->dev, mask); +} + +static int rcar_pci_dma_set_dma_mask(struct device *dev, u64 mask) +{ + struct rcar_pci_priv *p = rcar_pci_dev_to_priv(dev); + + return get_dma_ops(p->dev)->set_dma_mask(p->dev, mask); +} + +static struct dma_map_ops rcar_pci_dma_ops = { + .alloc = rcar_pci_dma_alloc, + .free = rcar_pci_dma_free, + .mmap = rcar_pci_dma_mmap, + .get_sgtable = rcar_pci_dma_get_sgtable, + .map_page = rcar_pci_dma_map_page, + .unmap_page = rcar_pci_dma_unmap_page, + .map_sg = rcar_pci_dma_map_sg, + .unmap_sg = rcar_pci_dma_unmap_sg, + .sync_single_for_cpu = rcar_pci_dma_sync_single_for_cpu, + .sync_single_for_device = rcar_pci_dma_sync_single_for_device, + .sync_sg_for_cpu = rcar_pci_dma_sync_sg_for_cpu, + .sync_sg_for_device = rcar_pci_dma_sync_sg_for_device, + .mapping_error = rcar_pci_dma_mapping_error, + .dma_supported = rcar_pci_dma_supported, + .set_dma_mask = rcar_pci_dma_set_dma_mask, +}; + +static int rcar_pci_bus_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct rcar_pci_priv *priv; + struct device *dev = data; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_sys_data *sys = pci_dev->sysdata; + + priv = container_of(nb, struct rcar_pci_priv, bus_notifier); + if (priv != sys->private_data) + return 0; + + if (action == BUS_NOTIFY_BIND_DRIVER) + set_dma_ops(dev, &rcar_pci_dma_ops); + + return 0; +} + +static void rcar_pci_add_bus(struct pci_bus *bus) +{ + struct pci_sys_data *sys = bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + + bus_register_notifier(&pci_bus_type, &priv->bus_notifier); +} + +static void rcar_pci_remove_bus(struct pci_bus *bus) +{ + struct pci_sys_data *sys = bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + + bus_unregister_notifier(&pci_bus_type, &priv->bus_notifier); +} + +static int rcar_pci_needs_bounce(struct device *dev, + dma_addr_t dma_addr, size_t size) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rcar_pci_priv *priv = platform_get_drvdata(pdev); + + if (dma_addr < priv->window_base) + return 1; + + if (dma_addr >= (priv->window_base + priv->window_size)) + return 1; + + return 0; +} + static int rcar_pci_probe(struct platform_device *pdev) { struct resource *cfg_res, *mem_res; struct rcar_pci_priv *priv; void __iomem *reg; struct hw_pci hw; + void *dma_virt; + int ret; cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, cfg_res); @@ -293,8 +520,35 @@ static int rcar_pci_probe(struct platfor priv->irq = platform_get_irq(pdev, 0); priv->reg = reg; priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + priv->bus_notifier.notifier_call = rcar_pci_bus_notify; priv->window_size = SZ_1G; + priv->dma_size = SZ_4M; + + /* allocate pool of memory guaranteed to be inside window */ + priv->dma_pool = devm_gen_pool_create(&pdev->dev, 7, -1); + if (!priv->dma_pool) + return -ENOMEM; + + dma_virt = dmam_alloc_coherent(&pdev->dev, priv->dma_size, + &priv->dma_phys, GFP_KERNEL); + if (!dma_virt) + return -ENOMEM; + + ret = gen_pool_add_virt(priv->dma_pool, (unsigned long)dma_virt, + priv->dma_phys, priv->dma_size, -1); + if (ret) + return ret; + + /* select window base address based on physical address for memory */ + priv->window_base = priv->dma_phys & ~(priv->window_size - 1); + + /* setup alloc()/free() to let dmabounce allocate from our pool */ + set_dma_ops(&pdev->dev, &rcar_pci_dma_ops_init); + + /* shared dmabounce for the PCI bridge */ + dmabounce_register_dev(&pdev->dev, 2048, 4096, rcar_pci_needs_bounce); memset(&hw, 0, sizeof(hw)); hw.nr_controllers = 1; @@ -302,6 +556,8 @@ static int rcar_pci_probe(struct platfor hw.map_irq = rcar_pci_map_irq; hw.ops = &rcar_pci_ops; hw.setup = rcar_pci_setup; + hw.add_bus = rcar_pci_add_bus; + hw.remove_bus = rcar_pci_remove_bus; pci_common_init_dev(&pdev->dev, &hw); return 0; } -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/