From: Konrad Rzeszutek Wilk <konrad.wilk@oracle•com>
To: Albert Herranz <albert_herranz@yahoo•es>
Cc: iommu@lists•linux-foundation.org, linux-usb@vger•kernel.org,
linuxppc-dev@lists•ozlabs.org
Subject: Re: [PATCH v5 08/10] wii: add mem2 dma mapping ops
Date: Fri, 19 Mar 2010 15:50:13 -0400 [thread overview]
Message-ID: <20100319195013.GC31908@phenom.dumpdata.com> (raw)
In-Reply-To: <1269023546-25534-9-git-send-email-albert_herranz@yahoo.es>
> +int wii_set_mem2_dma_constraints(struct device *dev)
> +{
> + struct dev_archdata *sd;
> +
> + sd = &dev->archdata;
> + sd->max_direct_dma_addr = 0;
> + sd->min_direct_dma_addr = wii_hole_start + wii_hole_size;
> +
> + set_dma_ops(dev, &wii_mem2_dma_ops);
> + return 0;
> +}
> +EXPORT_SYMBOL(wii_set_mem2_dma_constraints);
Can you make them EXPORT_SYMBOL_GPL?
> +
> +/**
> + * wii_clear_mem2_dma_constraints() - clears device MEM2 DMA constraints
> + * @dev: device for which DMA constraints are cleared
> + *
> + * Instructs device @dev to stop using MEM2 DMA buffers for DMA transfers.
> + * Must be called to undo wii_set_mem2_dma_constraints().
> + */
> +void wii_clear_mem2_dma_constraints(struct device *dev)
> +{
> + struct dev_archdata *sd;
> +
> + sd = &dev->archdata;
> + sd->max_direct_dma_addr = 0;
> + sd->min_direct_dma_addr = 0;
> +
> + set_dma_ops(dev, &dma_direct_ops);
> +}
> +EXPORT_SYMBOL(wii_clear_mem2_dma_constraints);
Ditto..
> +
> +/*
> + * swiotlb-based DMA ops for MEM2-only devices on the Wii.
> + *
> + */
> +
> +/*
> + * Allocate the SWIOTLB from MEM2.
> + */
> +void * __init swiotlb_alloc_boot(size_t size, unsigned long nslabs)
> +{
> + return __alloc_bootmem_low(size, PAGE_SIZE,
> + wii_hole_start + wii_hole_size);
> +}
> +
> +/*
> + * Bounce: copy the swiotlb buffer back to the original dma location
> + * This is a platform specific version replacing the generic __weak version.
> + */
> +void swiotlb_bounce(phys_addr_t phys, char *dma_buf, size_t size,
> + enum dma_data_direction dir)
> +{
> + void *vaddr = phys_to_virt(phys);
> +
> + if (dir == DMA_TO_DEVICE) {
> + memcpy(dma_buf, vaddr, size);
> + __dma_sync(dma_buf, size, dir);
> + } else {
> + __dma_sync(dma_buf, size, dir);
> + memcpy(vaddr, dma_buf, size);
> + }
> +}
> +
> +static dma_addr_t
> +mem2_virt_to_bus(struct device *dev, void *address)
> +{
> + return phys_to_dma(dev, virt_to_phys(address));
> +}
> +
> +static int
> +mem2_dma_mapping_error(struct device *dev, dma_addr_t dma_handle)
> +{
> + return dma_handle == mem2_virt_to_bus(dev, swiotlb_bk_overflow_buffer);
> +}
> +
> +static int
> +mem2_dma_supported(struct device *dev, u64 mask)
> +{
> + return mem2_virt_to_bus(dev, swiotlb_bk_end - 1) <= mask;
> +}
> +
> +/*
> + * Determines if a given DMA region specified by @dma_handle
> + * requires bouncing.
> + *
> + * Bouncing is required if the DMA region falls within MEM1.
> + */
> +static int mem2_needs_dmabounce(dma_addr_t dma_handle)
> +{
> + return dma_handle < wii_hole_start;
> +}
> +
> +/*
> + * Use the dma_direct_ops hooks for allocating and freeing coherent memory
> + * from the MEM2 DMA region.
> + */
> +
> +static void *mem2_alloc_coherent(struct device *dev, size_t size,
> + dma_addr_t *dma_handle, gfp_t gfp)
> +{
> + void *vaddr;
> +
> + vaddr = dma_direct_ops.alloc_coherent(wii_mem2_dma_dev(), size,
> + dma_handle, gfp);
> + if (vaddr && mem2_needs_dmabounce(*dma_handle)) {
> + dma_direct_ops.free_coherent(wii_mem2_dma_dev(), size, vaddr,
> + *dma_handle);
> + dev_err(dev, "failed to allocate MEM2 coherent memory\n");
> + vaddr = NULL;
> + }
> + return vaddr;
> +}
> +
> +static void mem2_free_coherent(struct device *dev, size_t size,
> + void *vaddr, dma_addr_t dma_handle)
> +{
> + dma_direct_ops.free_coherent(wii_mem2_dma_dev(), size, vaddr,
> + dma_handle);
> +}
> +
> +/*
> + * Maps (part of) a page so it can be safely accessed by a device.
> + *
> + * Calls the corresponding dma_direct_ops hook if the page region falls
> + * within MEM2.
> + * Otherwise, a bounce buffer allocated from MEM2 coherent memory is used.
> + */
> +static dma_addr_t
> +mem2_map_page(struct device *dev, struct page *page, unsigned long offset,
> + size_t size, enum dma_data_direction dir,
> + struct dma_attrs *attrs)
> +{
> + phys_addr_t phys = page_to_phys(page) + offset;
> + dma_addr_t dma_handle = phys_to_dma(dev, phys);
> + dma_addr_t swiotlb_start_dma;
> + void *map;
> +
> + BUG_ON(dir == DMA_NONE);
> +
> + if (dma_capable(dev, dma_handle, size) && !swiotlb_force) {
> + return dma_direct_ops.map_page(dev, page, offset, size,
> + dir, attrs);
> + }
> +
> + swiotlb_start_dma = mem2_virt_to_bus(dev, swiotlb_bk_start);
> + map = swiotlb_bk_map_single(dev, phys, swiotlb_start_dma, size, dir);
> + if (!map) {
> + swiotlb_full(dev, size, dir, 1);
> + map = swiotlb_bk_overflow_buffer;
> + }
> +
> + dma_handle = mem2_virt_to_bus(dev, map);
> + BUG_ON(!dma_capable(dev, dma_handle, size));
> +
> + return dma_handle;
> +}
> +
> +/*
> + * Unmaps (part of) a page previously mapped.
> + *
> + * Calls the corresponding dma_direct_ops hook if the DMA region associated
> + * to the dma handle @dma_handle wasn't bounced.
> + * Otherwise, the associated bounce buffer is de-bounced.
> + */
> +static void
> +mem2_unmap_page(struct device *dev, dma_addr_t dma_handle, size_t size,
> + enum dma_data_direction dir, struct dma_attrs *attrs)
> +{
> + swiotlb_unmap_page(dev, dma_handle, size, dir, attrs);
> +}
> +
> +/*
> + * Unmaps a scatter/gather list by unmapping each entry.
> + */
> +static void
> +mem2_unmap_sg(struct device *dev, struct scatterlist *sgl, int nents,
> + enum dma_data_direction dir, struct dma_attrs *attrs)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i)
> + mem2_unmap_page(dev, sg->dma_address, sg->length, dir, attrs);
> +}
> +
> +/*
> + * Maps a scatter/gather list by mapping each entry.
> + */
> +static int
> +mem2_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
> + enum dma_data_direction dir, struct dma_attrs *attrs)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i) {
> + sg->dma_length = sg->length;
> + sg->dma_address = mem2_map_page(dev, sg_page(sg), sg->offset,
> + sg->length, dir, attrs);
> + if (mem2_dma_mapping_error(dev, sg->dma_address)) {
> + mem2_unmap_sg(dev, sgl, i, dir, attrs);
> + nents = 0;
> + sgl[nents].dma_length = 0;
> + pr_debug("%s: mem2_map_page error\n", __func__);
Maybe use 'dev_err(dev," mem2..." ?
> + break;
> + }
> + }
> + return nents;
> +}
> +
> +/*
> + * The sync functions synchronize streaming mode DMA translations
> + * making physical memory consistent before/after a DMA transfer.
> + *
> + * They call the corresponding dma_direct_ops hook if the DMA region
> + * associated to the dma handle @dma_handle wasn't bounced.
> + * Otherwise, original DMA buffers and their matching bounce buffers are put
> + * in sync.
> + */
> +
> +static int
> +mem2_sync_range(struct device *dev, dma_addr_t dma_handle,
> + unsigned long offset, size_t size, int dir, int target)
> +{
> + phys_addr_t paddr = dma_to_phys(dev, dma_handle) + offset;
> + void *vaddr = phys_to_virt(paddr);
> +
> + BUG_ON(dir == DMA_NONE);
> +
> + if (is_swiotlb_buffer(paddr)) {
> + swiotlb_bk_sync_single(dev, vaddr, size, dir, target);
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void
> +mem2_sync_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
> + unsigned long offset, size_t size,
> + enum dma_data_direction dir)
> +{
> + int done = mem2_sync_range(dev, dma_handle, offset, size, dir,
> + SYNC_FOR_CPU);
> + if (!done) {
> + dma_direct_ops.sync_single_range_for_cpu(dev, dma_handle,
> + offset, size, dir);
> + }
> +}
> +
> +static void
> +mem2_sync_range_for_device(struct device *dev, dma_addr_t dma_handle,
> + unsigned long offset, size_t size,
> + enum dma_data_direction dir)
> +{
> + int done = mem2_sync_range(dev, dma_handle, offset, size, dir,
> + SYNC_FOR_DEVICE);
> + if (!done) {
> + dma_direct_ops.sync_single_range_for_device(dev, dma_handle,
> + offset, size, dir);
> + }
> +}
> +
> +static void
> +mem2_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, int nents,
> + enum dma_data_direction dir)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i) {
> + mem2_sync_range_for_cpu(dev, sg_dma_address(sg), sg->offset,
> + sg_dma_len(sg), dir);
> + }
> +}
> +
> +static void
> +mem2_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, int nents,
> + enum dma_data_direction dir)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i) {
> + mem2_sync_range_for_device(dev, sg_dma_address(sg), sg->offset,
> + sg_dma_len(sg), dir);
> + }
> +}
> +
> +/*
> + * Set of DMA operations for devices requiring MEM2 DMA buffers.
> + */
> +struct dma_map_ops wii_mem2_dma_ops = {
> + .alloc_coherent = mem2_alloc_coherent,
> + .free_coherent = mem2_free_coherent,
> + .map_sg = mem2_map_sg,
> + .unmap_sg = mem2_unmap_sg,
> + .dma_supported = mem2_dma_supported,
> + .map_page = mem2_map_page,
> + .unmap_page = mem2_unmap_page,
> + .sync_single_range_for_cpu = mem2_sync_range_for_cpu,
> + .sync_single_range_for_device = mem2_sync_range_for_device,
> + .sync_sg_for_cpu = mem2_sync_sg_for_cpu,
> + .sync_sg_for_device = mem2_sync_sg_for_device,
> + .mapping_error = mem2_dma_mapping_error,
> +};
> --
> 1.6.3.3
>
> _______________________________________________
> iommu mailing list
> iommu@lists•linux-foundation.org
> https://lists.linux-foundation.org/mailman/listinfo/iommu
next prev parent reply other threads:[~2010-03-19 20:20 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-03-19 18:32 [PATCH v5 00/10] wii: add usb 2.0 support Albert Herranz
2010-03-19 18:32 ` [PATCH v5 01/10] swiotbl: add back swiotlb_alloc_boot() Albert Herranz
2010-03-19 18:32 ` [PATCH v5 02/10] swiotlb: make swiotlb_bounce() __weak Albert Herranz
2010-03-19 18:32 ` [PATCH v5 03/10] powerpc: add per-device dma coherent support Albert Herranz
2010-03-19 18:32 ` [PATCH v5 04/10] powerpc: add min_direct_dma_addr Albert Herranz
2010-03-19 18:32 ` [PATCH v5 05/10] USB: refactor unmap_urb_for_dma/map_urb_for_dma Albert Herranz
2010-03-19 18:32 ` [PATCH v5 06/10] USB: add HCD_NO_COHERENT_MEM host controller driver flag Albert Herranz
2010-03-19 18:32 ` [PATCH v5 07/10] wii: have generic dma coherent Albert Herranz
2010-03-19 18:32 ` [PATCH v5 08/10] wii: add mem2 dma mapping ops Albert Herranz
2010-03-19 19:50 ` Konrad Rzeszutek Wilk [this message]
2010-03-20 0:59 ` Albert Herranz
2010-03-19 19:51 ` Konrad Rzeszutek Wilk
2010-03-20 0:58 ` Albert Herranz
2010-03-19 18:32 ` [PATCH v5 09/10] wii: enable swiotlb Albert Herranz
2010-03-19 18:32 ` [PATCH v5 10/10] wii: hollywood ehci controller support Albert Herranz
2010-03-19 19:01 ` [PATCH v5 00/10] wii: add usb 2.0 support Alan Stern
2010-03-19 19:13 ` Albert Herranz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100319195013.GC31908@phenom.dumpdata.com \
--to=konrad.wilk@oracle$(echo .)com \
--cc=albert_herranz@yahoo$(echo .)es \
--cc=iommu@lists$(echo .)linux-foundation.org \
--cc=linux-usb@vger$(echo .)kernel.org \
--cc=linuxppc-dev@lists$(echo .)ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox