Re: About the "USB Cache and busdma usage in USB" thread

From: Grzegorz Bernacki <gjb_at_semihalf.com>
Date: Wed, 05 Aug 2009 15:17:18 +0200
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
_at__at_ -678,8 +678,8 _at__at_ 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);
  }


/*------------------------------------------------------------------------*
_at__at_ -692,8 +692,7 _at__at_ 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);
  }


/*------------------------------------------------------------------------*
Received on Wed Aug 05 2009 - 11:17:20 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:39:53 UTC