/* Implement as transfer_fifo2linear + transfer_linear2fifo. */ch->io_fn[0](ch->io_opaque[0],fifo_buf,ch->bytes);ch->io_fn[1](ch->io_opaque[1],fifo_buf,ch->bytes);}structdma_s{structsoc_dma_ssoc;intchnum;uint64_tch_enable_mask;int64_tchannel_freq;intenabled_count;structmemmap_entry_s{enumsoc_dma_port_typetype;target_phys_addr_taddr;union{struct{void*opaque;soc_dma_io_tfn;intout;}fifo;struct{void*base;size_tsize;}mem;}u;}*memmap;intmemmap_size;structsoc_dma_ch_sch[0];};staticvoidsoc_dma_ch_schedule(structsoc_dma_ch_s*ch,intdelay_bytes){int64_tnow=qemu_get_clock(vm_clock);structdma_s*dma=(structdma_s*)ch->dma;qemu_mod_timer(ch->timer,now+delay_bytes/dma->channel_freq);}staticvoidsoc_dma_ch_run(void*opaque){structsoc_dma_ch_s*ch=(structsoc_dma_ch_s*)opaque;ch->running=1;ch->dma->setup_fn(ch);ch->transfer_fn(ch);ch->running=0;if(ch->enable)soc_dma_ch_schedule(ch,ch->bytes);ch->bytes=0;}staticinlinestructmemmap_entry_s*soc_dma_lookup(structdma_s*dma,target_phys_addr_taddr){structmemmap_entry_s*lo;inthi;lo=dma->memmap;hi=dma->memmap_size;while(hi>1){hi/=2;if(lo[hi].addr<=addr)lo+=hi;}returnlo;}staticinlineenumsoc_dma_port_typesoc_dma_ch_update_type(structsoc_dma_ch_s*ch,intport){structdma_s*dma=(structdma_s*)ch->dma;structmemmap_entry_s*entry=soc_dma_lookup(dma,ch->vaddr[port]);if(entry->type==soc_dma_port_fifo){while(entry<dma->memmap+dma->memmap_size&&entry->u.fifo.out!=port)entry++;if(entry->addr!=ch->vaddr[port]||entry->u.fifo.out!=port)returnsoc_dma_port_other;if(ch->type[port]!=soc_dma_access_const)returnsoc_dma_port_other;ch->io_fn[port]=entry->u.fifo.fn;ch->io_opaque[port]=entry->u.fifo.opaque;returnsoc_dma_port_fifo;}elseif(entry->type==soc_dma_port_mem){if(entry->addr>ch->vaddr[port]||entry->addr+entry->u.mem.size<=ch->vaddr[port])returnsoc_dma_port_other;/*TODO:supportconstantmemoryaddressforsourceportasusedfor*drawingsolidrectanglesbyPalmOS(R).*/if(ch->type[port]!=soc_dma_access_const)returnsoc_dma_port_other;ch->paddr[port]=(uint8_t*)entry->u.mem.base+(ch->vaddr[port]-entry->addr);/*TODO:savebyteslefttotheendofthemappingsomewheresowe*cancheckwe'renotreadingbeyondit.*/returnsoc_dma_port_mem;}elsereturnsoc_dma_port_other;}voidsoc_dma_ch_update(structsoc_dma_ch_s*ch){enumsoc_dma_port_typesrc,dst;src=soc_dma_ch_update_type(ch,0);if(src==soc_dma_port_other){ch->update=0;ch->transfer_fn=ch->dma->transfer_fn;return;}dst=soc_dma_ch_update_type(ch,1);/* TODO: use src and dst as array indices. */if(src==soc_dma_port_mem&&dst==soc_dma_port_mem)ch->transfer_fn=transfer_mem2mem;elseif(src==soc_dma_port_mem&&dst==soc_dma_port_fifo)ch->transfer_fn=transfer_mem2fifo;elseif(src==soc_dma_port_fifo&&dst==soc_dma_port_mem)ch->transfer_fn=transfer_fifo2mem;elseif(src==soc_dma_port_fifo&&dst==soc_dma_port_fifo)ch->transfer_fn=transfer_fifo2fifo;elsech->transfer_fn=ch->dma->transfer_fn;ch->update=(dst!=soc_dma_port_other);}staticvoidsoc_dma_ch_freq_update(structdma_s*s){if(s->enabled_count)/* We completely ignore channel priorities and stuff */s->channel_freq=s->soc.freq/s->enabled_count;else/*TODO:Signalthatwewanttodisablethefunctionalclockandlet*theplatformcodedecidewhattodowithit,i.e.checkthat*auto-idleisenabledintheclockcontrollerandifwearestopping*theclock,dothesamewithanyparentclocksthathadonlyone*userkeepingthemonandauto-idleenabled.*/;}voidsoc_dma_set_request(structsoc_dma_ch_s*ch,intlevel){structdma_s*dma=(structdma_s*)ch->dma;dma->enabled_count+=level-ch->enable;if(level)dma->ch_enable_mask|=1<<ch->num;elsedma->ch_enable_mask&=~(1<<ch->num);if(level!=ch->enable){soc_dma_ch_freq_update(dma);ch->enable=level;if(!ch->enable)qemu_del_timer(ch->timer);elseif(!ch->running)soc_dma_ch_run(ch);elsesoc_dma_ch_schedule(ch,1);}}voidsoc_dma_reset(structsoc_dma_s*soc){structdma_s*s=(structdma_s*)soc;s->soc.drqbmp=0;s->ch_enable_mask=0;s->enabled_count=0;soc_dma_ch_freq_update(s);}/* TODO: take a functional-clock argument */structsoc_dma_s*soc_dma_init(intn){inti;structdma_s*s=qemu_mallocz(sizeof(*s)+n*sizeof(*s->ch));s->chnum=n;s->soc.ch=s->ch;for(i=0;i<n;i++){s->ch[i].dma=&s->soc;s->ch[i].num=i;s->ch[i].timer=qemu_new_timer(vm_clock,soc_dma_ch_run,&s->ch[i]);}soc_dma_reset(&s->soc);
(dma->memmap_size+1));entry=soc_dma_lookup(dma,virt_base);if(dma->memmap_size){if(entry->type==soc_dma_port_mem){if(entry->addr<=virt_base&&entry->addr+entry->u.mem.size>virt_base){fprintf(stderr,"%s: FIFO at "TARGET_FMT_lx" collides with RAM region at "TARGET_FMT_lx"-"TARGET_FMT_lx"\n",__FUNCTION__,(target_ulong)virt_base,(target_ulong)entry->addr,(target_ulong)(entry->addr+entry->u.mem.size));exit(-1);}if(entry->addr<=virt_base)entry++;}elsewhile(entry<dma->memmap+dma->memmap_size&&entry->addr<=virt_base){if(entry->addr==virt_base&&entry->u.fifo.out==out){fprintf(stderr,"%s: FIFO at "TARGET_FMT_lx" collides FIFO at "TARGET_FMT_lx"\n",__FUNCTION__,(target_ulong)virt_base,(target_ulong)entry->addr);exit(-1);}entry++;}memmove(entry+1,entry,(uint8_t*)(dma->memmap+dma->memmap_size++)-(uint8_t*)entry);}elsedma->memmap_size++;entry->addr=virt_base;entry->type=soc_dma_port_fifo;entry->u.fifo.fn=fn;entry->u.fifo.opaque=opaque;entry->u.fifo.out=out;}voidsoc_dma_port_add_mem(structsoc_dma_s*soc,uint8_t*phys_base,target_phys_addr_tvirt_base,size_tsize){structmemmap_entry_s*entry;structdma_s*dma=(structdma_s*)soc;
(dma->memmap_size+1));entry=soc_dma_lookup(dma,virt_base);if(dma->memmap_size){if(entry->type==soc_dma_port_mem){if((entry->addr>=virt_base&&entry->addr<virt_base+size)||(entry->addr<=virt_base&&entry->addr+entry->u.mem.size>virt_base)){fprintf(stderr,"%s: RAM at "TARGET_FMT_lx"-"TARGET_FMT_lx" collides with RAM region at "TARGET_FMT_lx"-"TARGET_FMT_lx"\n",__FUNCTION__,(target_ulong)virt_base,(target_ulong)(virt_base+size),(target_ulong)entry->addr,(target_ulong)(entry->addr+entry->u.mem.size));exit(-1);}if(entry->addr<=virt_base)entry++;}else{if(entry->addr>=virt_base&&entry->addr<virt_base+size){fprintf(stderr,"%s: RAM at "TARGET_FMT_lx"-"TARGET_FMT_lx" collides with FIFO at "TARGET_FMT_lx"\n",__FUNCTION__,(target_ulong)virt_base,(target_ulong)(virt_base+size),(target_ulong)entry->addr);exit(-1);}while(entry<dma->memmap+dma->memmap_size&&entry->addr<=virt_base)entry++;}memmove(entry+1,entry,(uint8_t*)(dma->memmap+dma->memmap_size++)-(uint8_t*)entry);}elsedma->memmap_size++;entry->addr=virt_base;entry->type=soc_dma_port_mem;entry->u.mem.base=phys_base;entry->u.mem.size=size;}/* TODO: port removal for ports like PCMCIA memory */