About the "USB Cache and busdma usage in USB" thread
Grzegorz Bernacki
gjb at semihalf.com
Wed Aug 5 13:17:21 UTC 2009
Hans Petter Selasky wrote:
> There are two kinds of DMA memory in USB regard:
> 1) Transfer descriptors are allocated in coherent DMA memory.
> Operation logic:
>
> 1.a) Write to descriptor.
> 1.b.0) Call usb_pc_cpu_flush() to write data to RAM.
> 1.b.1) Write more fields to descriptor.
> 1.b.2) Call usb_pc_cpu_flush() to write data to RAM.
> 1.c) Call usb_pc_cpu_invalidate() to clear cache.
> 1.d) Read status field. If not complete goto 1.c)
>
> 2) Any kernel virtual memory (which might not be coherent)
>
> 2.a.0) CPU read case:
> 2.a.1) Before transfer start usb_pc_cpu_invalidate() is called to clear any
> data in cache for this buffer.
> 2.a.2) After transfer completion usb_pc_cpu_invalidate() is called again.
>
> 2.b.0) CPU write case:
> 2.b.1) Before transfer start usb_pc_cpu_flush() is called to to flush any data
> in cache to RAM for this buffer.
> 2.b.2) After transfer completion there is no cache operation.
>
The best solution is to use bus_dmamap_sync() in in conventional way. I
mean call bus_dmamap_sync(..., BUS_DMASYNC_PREREAD) in case 2.a.1 and
bus_dmamap_sync(..., BUS_DMASYNC_POSTREAD) in cases 2.a.2 and 1.c. But
this is quite a big change and it's risky to put in into -current now,
so below is another solution which I believe is simple and safe.
I understand that usb_pc_cpu_flush() is called *before* write
transfer. So I think that we can just call bus_dmamap_sync(pc->tag,
pc->map, BUS_DMASYNC_PREWRITE) there.
usb_pc_cpu_invalidate() is called before and after each read transfer
and to invalidate cache before reading status field.
So I think that simplest fix is to call following sequence of functions
in it:
bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_POSTREAD);
bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREREAD);
Below is the patch with that solution. I tested it on ARM and PowerPC
and it fixes the problem. Please test it on other platforms you have to
see if there is no regression.
diff --git a/sys/dev/usb/usb_busdma.c b/sys/dev/usb/usb_busdma.c
index 82d18a1..c57f51d 100644
--- a/sys/dev/usb/usb_busdma.c
+++ b/sys/dev/usb/usb_busdma.c
@@ -678,8 +678,8 @@ usb_pc_cpu_invalidate(struct usb_page_cache *pc)
/* nothing has been loaded into this page cache! */
return;
}
- bus_dmamap_sync(pc->tag, pc->map,
- BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREREAD);
}
/*------------------------------------------------------------------------*
@@ -692,8 +692,7 @@ usb_pc_cpu_flush(struct usb_page_cache *pc)
/* nothing has been loaded into this page cache! */
return;
}
- bus_dmamap_sync(pc->tag, pc->map,
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREWRITE);
}
/*------------------------------------------------------------------------*
More information about the freebsd-current
mailing list