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.com \
--cc=albert_herranz@yahoo.es \
--cc=iommu@lists.linux-foundation.org \
--cc=linux-usb@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.