Re: [PATCH v5 08/10] wii: add mem2 dma mapping ops
From: Konrad Rzeszutek Wilk <hidden>
Date: 2010-03-19 20:20:33
+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