13cbee15bSj_mayer /* 23cbee15bSj_mayer * PowerMac descriptor-based DMA emulation 33cbee15bSj_mayer * 43cbee15bSj_mayer * Copyright (c) 2005-2007 Fabrice Bellard 53cbee15bSj_mayer * Copyright (c) 2007 Jocelyn Mayer 628ce5ce6Saurel32 * Copyright (c) 2009 Laurent Vivier 728ce5ce6Saurel32 * 828ce5ce6Saurel32 * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h 928ce5ce6Saurel32 * 1028ce5ce6Saurel32 * Definitions for using the Apple Descriptor-Based DMA controller 1128ce5ce6Saurel32 * in Power Macintosh computers. 1228ce5ce6Saurel32 * 1328ce5ce6Saurel32 * Copyright (C) 1996 Paul Mackerras. 1428ce5ce6Saurel32 * 1528ce5ce6Saurel32 * some parts from mol 0.9.71 1628ce5ce6Saurel32 * 1728ce5ce6Saurel32 * Descriptor based DMA emulation 1828ce5ce6Saurel32 * 1928ce5ce6Saurel32 * Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se) 203cbee15bSj_mayer * 213cbee15bSj_mayer * Permission is hereby granted, free of charge, to any person obtaining a copy 223cbee15bSj_mayer * of this software and associated documentation files (the "Software"), to deal 233cbee15bSj_mayer * in the Software without restriction, including without limitation the rights 243cbee15bSj_mayer * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 253cbee15bSj_mayer * copies of the Software, and to permit persons to whom the Software is 263cbee15bSj_mayer * furnished to do so, subject to the following conditions: 273cbee15bSj_mayer * 283cbee15bSj_mayer * The above copyright notice and this permission notice shall be included in 293cbee15bSj_mayer * all copies or substantial portions of the Software. 303cbee15bSj_mayer * 313cbee15bSj_mayer * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 323cbee15bSj_mayer * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 333cbee15bSj_mayer * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 343cbee15bSj_mayer * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 353cbee15bSj_mayer * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 363cbee15bSj_mayer * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 373cbee15bSj_mayer * THE SOFTWARE. 383cbee15bSj_mayer */ 3983c9f4caSPaolo Bonzini #include "hw/hw.h" 400d09e41aSPaolo Bonzini #include "hw/isa/isa.h" 410d09e41aSPaolo Bonzini #include "hw/ppc/mac_dbdma.h" 421de7afc9SPaolo Bonzini #include "qemu/main-loop.h" 433cbee15bSj_mayer 44ea026b2fSblueswir1 /* debug DBDMA */ 45ea026b2fSblueswir1 //#define DEBUG_DBDMA 46ea026b2fSblueswir1 47ea026b2fSblueswir1 #ifdef DEBUG_DBDMA 48001faf32SBlue Swirl #define DBDMA_DPRINTF(fmt, ...) \ 49001faf32SBlue Swirl do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0) 50ea026b2fSblueswir1 #else 51001faf32SBlue Swirl #define DBDMA_DPRINTF(fmt, ...) 52ea026b2fSblueswir1 #endif 53ea026b2fSblueswir1 5428ce5ce6Saurel32 /* 5528ce5ce6Saurel32 */ 563cbee15bSj_mayer 57d2f0ce21SAlexander Graf static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) 58d2f0ce21SAlexander Graf { 59d2f0ce21SAlexander Graf return container_of(ch, DBDMAState, channels[ch->channel]); 60d2f0ce21SAlexander Graf } 61d2f0ce21SAlexander Graf 6228ce5ce6Saurel32 #ifdef DEBUG_DBDMA 6328ce5ce6Saurel32 static void dump_dbdma_cmd(dbdma_cmd *cmd) 643cbee15bSj_mayer { 6528ce5ce6Saurel32 printf("dbdma_cmd %p\n", cmd); 6628ce5ce6Saurel32 printf(" req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); 6728ce5ce6Saurel32 printf(" command 0x%04x\n", le16_to_cpu(cmd->command)); 6828ce5ce6Saurel32 printf(" phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); 6928ce5ce6Saurel32 printf(" cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); 7028ce5ce6Saurel32 printf(" res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); 7128ce5ce6Saurel32 printf(" xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status)); 7228ce5ce6Saurel32 } 7328ce5ce6Saurel32 #else 7428ce5ce6Saurel32 static void dump_dbdma_cmd(dbdma_cmd *cmd) 7528ce5ce6Saurel32 { 7628ce5ce6Saurel32 } 7728ce5ce6Saurel32 #endif 7828ce5ce6Saurel32 static void dbdma_cmdptr_load(DBDMA_channel *ch) 7928ce5ce6Saurel32 { 8028ce5ce6Saurel32 DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n", 81ad674e53SAurelien Jarno ch->regs[DBDMA_CMDPTR_LO]); 82ad674e53SAurelien Jarno cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO], 83e1fe50dcSStefan Weil &ch->current, sizeof(dbdma_cmd)); 843cbee15bSj_mayer } 853cbee15bSj_mayer 8628ce5ce6Saurel32 static void dbdma_cmdptr_save(DBDMA_channel *ch) 873cbee15bSj_mayer { 8828ce5ce6Saurel32 DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n", 89ad674e53SAurelien Jarno ch->regs[DBDMA_CMDPTR_LO]); 9028ce5ce6Saurel32 DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n", 9128ce5ce6Saurel32 le16_to_cpu(ch->current.xfer_status), 9228ce5ce6Saurel32 le16_to_cpu(ch->current.res_count)); 93ad674e53SAurelien Jarno cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO], 94e1fe50dcSStefan Weil &ch->current, sizeof(dbdma_cmd)); 9528ce5ce6Saurel32 } 9628ce5ce6Saurel32 9728ce5ce6Saurel32 static void kill_channel(DBDMA_channel *ch) 9828ce5ce6Saurel32 { 9928ce5ce6Saurel32 DBDMA_DPRINTF("kill_channel\n"); 10028ce5ce6Saurel32 101ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] |= DEAD; 102ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~ACTIVE; 10328ce5ce6Saurel32 10428ce5ce6Saurel32 qemu_irq_raise(ch->irq); 10528ce5ce6Saurel32 } 10628ce5ce6Saurel32 10728ce5ce6Saurel32 static void conditional_interrupt(DBDMA_channel *ch) 10828ce5ce6Saurel32 { 10928ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 11028ce5ce6Saurel32 uint16_t intr; 11128ce5ce6Saurel32 uint16_t sel_mask, sel_value; 11228ce5ce6Saurel32 uint32_t status; 11328ce5ce6Saurel32 int cond; 11428ce5ce6Saurel32 11533ce36bbSAlexander Graf DBDMA_DPRINTF("%s\n", __func__); 11628ce5ce6Saurel32 117b42ec42dSaurel32 intr = le16_to_cpu(current->command) & INTR_MASK; 11828ce5ce6Saurel32 11928ce5ce6Saurel32 switch(intr) { 12028ce5ce6Saurel32 case INTR_NEVER: /* don't interrupt */ 12128ce5ce6Saurel32 return; 12228ce5ce6Saurel32 case INTR_ALWAYS: /* always interrupt */ 12328ce5ce6Saurel32 qemu_irq_raise(ch->irq); 12433ce36bbSAlexander Graf DBDMA_DPRINTF("%s: raise\n", __func__); 12528ce5ce6Saurel32 return; 12628ce5ce6Saurel32 } 12728ce5ce6Saurel32 128ad674e53SAurelien Jarno status = ch->regs[DBDMA_STATUS] & DEVSTAT; 12928ce5ce6Saurel32 130ad674e53SAurelien Jarno sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f; 131ad674e53SAurelien Jarno sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f; 13228ce5ce6Saurel32 13328ce5ce6Saurel32 cond = (status & sel_mask) == (sel_value & sel_mask); 13428ce5ce6Saurel32 13528ce5ce6Saurel32 switch(intr) { 13628ce5ce6Saurel32 case INTR_IFSET: /* intr if condition bit is 1 */ 13733ce36bbSAlexander Graf if (cond) { 13828ce5ce6Saurel32 qemu_irq_raise(ch->irq); 13933ce36bbSAlexander Graf DBDMA_DPRINTF("%s: raise\n", __func__); 14033ce36bbSAlexander Graf } 14128ce5ce6Saurel32 return; 14228ce5ce6Saurel32 case INTR_IFCLR: /* intr if condition bit is 0 */ 14333ce36bbSAlexander Graf if (!cond) { 14428ce5ce6Saurel32 qemu_irq_raise(ch->irq); 14533ce36bbSAlexander Graf DBDMA_DPRINTF("%s: raise\n", __func__); 14633ce36bbSAlexander Graf } 14728ce5ce6Saurel32 return; 14828ce5ce6Saurel32 } 14928ce5ce6Saurel32 } 15028ce5ce6Saurel32 15128ce5ce6Saurel32 static int conditional_wait(DBDMA_channel *ch) 15228ce5ce6Saurel32 { 15328ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 15428ce5ce6Saurel32 uint16_t wait; 15528ce5ce6Saurel32 uint16_t sel_mask, sel_value; 15628ce5ce6Saurel32 uint32_t status; 15728ce5ce6Saurel32 int cond; 15828ce5ce6Saurel32 15928ce5ce6Saurel32 DBDMA_DPRINTF("conditional_wait\n"); 16028ce5ce6Saurel32 161b42ec42dSaurel32 wait = le16_to_cpu(current->command) & WAIT_MASK; 16228ce5ce6Saurel32 16328ce5ce6Saurel32 switch(wait) { 16428ce5ce6Saurel32 case WAIT_NEVER: /* don't wait */ 16528ce5ce6Saurel32 return 0; 16628ce5ce6Saurel32 case WAIT_ALWAYS: /* always wait */ 16728ce5ce6Saurel32 return 1; 16828ce5ce6Saurel32 } 16928ce5ce6Saurel32 170ad674e53SAurelien Jarno status = ch->regs[DBDMA_STATUS] & DEVSTAT; 17128ce5ce6Saurel32 172ad674e53SAurelien Jarno sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f; 173ad674e53SAurelien Jarno sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f; 17428ce5ce6Saurel32 17528ce5ce6Saurel32 cond = (status & sel_mask) == (sel_value & sel_mask); 17628ce5ce6Saurel32 17728ce5ce6Saurel32 switch(wait) { 17828ce5ce6Saurel32 case WAIT_IFSET: /* wait if condition bit is 1 */ 17928ce5ce6Saurel32 if (cond) 18028ce5ce6Saurel32 return 1; 18128ce5ce6Saurel32 return 0; 18228ce5ce6Saurel32 case WAIT_IFCLR: /* wait if condition bit is 0 */ 18328ce5ce6Saurel32 if (!cond) 18428ce5ce6Saurel32 return 1; 18528ce5ce6Saurel32 return 0; 18628ce5ce6Saurel32 } 18728ce5ce6Saurel32 return 0; 18828ce5ce6Saurel32 } 18928ce5ce6Saurel32 19028ce5ce6Saurel32 static void next(DBDMA_channel *ch) 19128ce5ce6Saurel32 { 19228ce5ce6Saurel32 uint32_t cp; 19328ce5ce6Saurel32 194ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~BT; 19528ce5ce6Saurel32 196ad674e53SAurelien Jarno cp = ch->regs[DBDMA_CMDPTR_LO]; 197ad674e53SAurelien Jarno ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd); 19828ce5ce6Saurel32 dbdma_cmdptr_load(ch); 19928ce5ce6Saurel32 } 20028ce5ce6Saurel32 20128ce5ce6Saurel32 static void branch(DBDMA_channel *ch) 20228ce5ce6Saurel32 { 20328ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 20428ce5ce6Saurel32 20528ce5ce6Saurel32 ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep; 206ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] |= BT; 20728ce5ce6Saurel32 dbdma_cmdptr_load(ch); 20828ce5ce6Saurel32 } 20928ce5ce6Saurel32 21028ce5ce6Saurel32 static void conditional_branch(DBDMA_channel *ch) 21128ce5ce6Saurel32 { 21228ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 21328ce5ce6Saurel32 uint16_t br; 21428ce5ce6Saurel32 uint16_t sel_mask, sel_value; 21528ce5ce6Saurel32 uint32_t status; 21628ce5ce6Saurel32 int cond; 21728ce5ce6Saurel32 21828ce5ce6Saurel32 DBDMA_DPRINTF("conditional_branch\n"); 21928ce5ce6Saurel32 22028ce5ce6Saurel32 /* check if we must branch */ 22128ce5ce6Saurel32 222b42ec42dSaurel32 br = le16_to_cpu(current->command) & BR_MASK; 22328ce5ce6Saurel32 22428ce5ce6Saurel32 switch(br) { 22528ce5ce6Saurel32 case BR_NEVER: /* don't branch */ 22628ce5ce6Saurel32 next(ch); 22728ce5ce6Saurel32 return; 22828ce5ce6Saurel32 case BR_ALWAYS: /* always branch */ 22928ce5ce6Saurel32 branch(ch); 23028ce5ce6Saurel32 return; 23128ce5ce6Saurel32 } 23228ce5ce6Saurel32 233ad674e53SAurelien Jarno status = ch->regs[DBDMA_STATUS] & DEVSTAT; 23428ce5ce6Saurel32 235ad674e53SAurelien Jarno sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f; 236ad674e53SAurelien Jarno sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f; 23728ce5ce6Saurel32 23828ce5ce6Saurel32 cond = (status & sel_mask) == (sel_value & sel_mask); 23928ce5ce6Saurel32 24028ce5ce6Saurel32 switch(br) { 24128ce5ce6Saurel32 case BR_IFSET: /* branch if condition bit is 1 */ 24228ce5ce6Saurel32 if (cond) 24328ce5ce6Saurel32 branch(ch); 24428ce5ce6Saurel32 else 24528ce5ce6Saurel32 next(ch); 24628ce5ce6Saurel32 return; 24728ce5ce6Saurel32 case BR_IFCLR: /* branch if condition bit is 0 */ 24828ce5ce6Saurel32 if (!cond) 24928ce5ce6Saurel32 branch(ch); 25028ce5ce6Saurel32 else 25128ce5ce6Saurel32 next(ch); 25228ce5ce6Saurel32 return; 25328ce5ce6Saurel32 } 25428ce5ce6Saurel32 } 25528ce5ce6Saurel32 256b42ec42dSaurel32 static void channel_run(DBDMA_channel *ch); 257b42ec42dSaurel32 258b42ec42dSaurel32 static void dbdma_end(DBDMA_io *io) 25928ce5ce6Saurel32 { 26028ce5ce6Saurel32 DBDMA_channel *ch = io->channel; 26128ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 26228ce5ce6Saurel32 26333ce36bbSAlexander Graf DBDMA_DPRINTF("%s\n", __func__); 26433ce36bbSAlexander Graf 265b42ec42dSaurel32 if (conditional_wait(ch)) 266b42ec42dSaurel32 goto wait; 26728ce5ce6Saurel32 268ad674e53SAurelien Jarno current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); 269ad674e53SAurelien Jarno current->res_count = cpu_to_le16(io->len); 270b42ec42dSaurel32 dbdma_cmdptr_save(ch); 271862c9280Saurel32 if (io->is_last) 272ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~FLUSH; 27328ce5ce6Saurel32 274b42ec42dSaurel32 conditional_interrupt(ch); 275b42ec42dSaurel32 conditional_branch(ch); 276b42ec42dSaurel32 277b42ec42dSaurel32 wait: 27803ee3b1eSAlexander Graf /* Indicate that we're ready for a new DMA round */ 27903ee3b1eSAlexander Graf ch->io.processing = false; 28003ee3b1eSAlexander Graf 281ad674e53SAurelien Jarno if ((ch->regs[DBDMA_STATUS] & RUN) && 282ad674e53SAurelien Jarno (ch->regs[DBDMA_STATUS] & ACTIVE)) 283b42ec42dSaurel32 channel_run(ch); 28428ce5ce6Saurel32 } 28528ce5ce6Saurel32 286b42ec42dSaurel32 static void start_output(DBDMA_channel *ch, int key, uint32_t addr, 28728ce5ce6Saurel32 uint16_t req_count, int is_last) 28828ce5ce6Saurel32 { 28928ce5ce6Saurel32 DBDMA_DPRINTF("start_output\n"); 29028ce5ce6Saurel32 29128ce5ce6Saurel32 /* KEY_REGS, KEY_DEVICE and KEY_STREAM 29228ce5ce6Saurel32 * are not implemented in the mac-io chip 29328ce5ce6Saurel32 */ 29428ce5ce6Saurel32 29528ce5ce6Saurel32 DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); 29628ce5ce6Saurel32 if (!addr || key > KEY_STREAM3) { 29728ce5ce6Saurel32 kill_channel(ch); 298b42ec42dSaurel32 return; 29928ce5ce6Saurel32 } 30028ce5ce6Saurel32 301b42ec42dSaurel32 ch->io.addr = addr; 30228ce5ce6Saurel32 ch->io.len = req_count; 30328ce5ce6Saurel32 ch->io.is_last = is_last; 304b42ec42dSaurel32 ch->io.dma_end = dbdma_end; 305b42ec42dSaurel32 ch->io.is_dma_out = 1; 30603ee3b1eSAlexander Graf ch->io.processing = true; 307a9ceb76dSAlexander Graf if (ch->rw) { 308b42ec42dSaurel32 ch->rw(&ch->io); 30928ce5ce6Saurel32 } 310a9ceb76dSAlexander Graf } 31128ce5ce6Saurel32 312b42ec42dSaurel32 static void start_input(DBDMA_channel *ch, int key, uint32_t addr, 31328ce5ce6Saurel32 uint16_t req_count, int is_last) 31428ce5ce6Saurel32 { 31528ce5ce6Saurel32 DBDMA_DPRINTF("start_input\n"); 31628ce5ce6Saurel32 31728ce5ce6Saurel32 /* KEY_REGS, KEY_DEVICE and KEY_STREAM 31828ce5ce6Saurel32 * are not implemented in the mac-io chip 31928ce5ce6Saurel32 */ 32028ce5ce6Saurel32 32133ce36bbSAlexander Graf DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); 32228ce5ce6Saurel32 if (!addr || key > KEY_STREAM3) { 32328ce5ce6Saurel32 kill_channel(ch); 324b42ec42dSaurel32 return; 32528ce5ce6Saurel32 } 32628ce5ce6Saurel32 327b42ec42dSaurel32 ch->io.addr = addr; 32828ce5ce6Saurel32 ch->io.len = req_count; 32928ce5ce6Saurel32 ch->io.is_last = is_last; 330b42ec42dSaurel32 ch->io.dma_end = dbdma_end; 331b42ec42dSaurel32 ch->io.is_dma_out = 0; 33203ee3b1eSAlexander Graf ch->io.processing = true; 333a9ceb76dSAlexander Graf if (ch->rw) { 334b42ec42dSaurel32 ch->rw(&ch->io); 33528ce5ce6Saurel32 } 336a9ceb76dSAlexander Graf } 33728ce5ce6Saurel32 338b42ec42dSaurel32 static void load_word(DBDMA_channel *ch, int key, uint32_t addr, 33928ce5ce6Saurel32 uint16_t len) 34028ce5ce6Saurel32 { 34128ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 34228ce5ce6Saurel32 uint32_t val; 34328ce5ce6Saurel32 34428ce5ce6Saurel32 DBDMA_DPRINTF("load_word\n"); 34528ce5ce6Saurel32 34628ce5ce6Saurel32 /* only implements KEY_SYSTEM */ 34728ce5ce6Saurel32 34828ce5ce6Saurel32 if (key != KEY_SYSTEM) { 34928ce5ce6Saurel32 printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key); 35028ce5ce6Saurel32 kill_channel(ch); 351b42ec42dSaurel32 return; 35228ce5ce6Saurel32 } 35328ce5ce6Saurel32 354e1fe50dcSStefan Weil cpu_physical_memory_read(addr, &val, len); 35528ce5ce6Saurel32 35628ce5ce6Saurel32 if (len == 2) 35728ce5ce6Saurel32 val = (val << 16) | (current->cmd_dep & 0x0000ffff); 35828ce5ce6Saurel32 else if (len == 1) 35928ce5ce6Saurel32 val = (val << 24) | (current->cmd_dep & 0x00ffffff); 36028ce5ce6Saurel32 36128ce5ce6Saurel32 current->cmd_dep = val; 36228ce5ce6Saurel32 36328ce5ce6Saurel32 if (conditional_wait(ch)) 364b42ec42dSaurel32 goto wait; 36528ce5ce6Saurel32 366ad674e53SAurelien Jarno current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); 36728ce5ce6Saurel32 dbdma_cmdptr_save(ch); 368ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~FLUSH; 36928ce5ce6Saurel32 37028ce5ce6Saurel32 conditional_interrupt(ch); 37128ce5ce6Saurel32 next(ch); 37228ce5ce6Saurel32 373b42ec42dSaurel32 wait: 374d2f0ce21SAlexander Graf DBDMA_kick(dbdma_from_ch(ch)); 37528ce5ce6Saurel32 } 37628ce5ce6Saurel32 377b42ec42dSaurel32 static void store_word(DBDMA_channel *ch, int key, uint32_t addr, 37828ce5ce6Saurel32 uint16_t len) 37928ce5ce6Saurel32 { 38028ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 38128ce5ce6Saurel32 uint32_t val; 38228ce5ce6Saurel32 38328ce5ce6Saurel32 DBDMA_DPRINTF("store_word\n"); 38428ce5ce6Saurel32 38528ce5ce6Saurel32 /* only implements KEY_SYSTEM */ 38628ce5ce6Saurel32 38728ce5ce6Saurel32 if (key != KEY_SYSTEM) { 38828ce5ce6Saurel32 printf("DBDMA: STORE_WORD, unimplemented key %x\n", key); 38928ce5ce6Saurel32 kill_channel(ch); 390b42ec42dSaurel32 return; 39128ce5ce6Saurel32 } 39228ce5ce6Saurel32 39328ce5ce6Saurel32 val = current->cmd_dep; 39428ce5ce6Saurel32 if (len == 2) 39528ce5ce6Saurel32 val >>= 16; 39628ce5ce6Saurel32 else if (len == 1) 39728ce5ce6Saurel32 val >>= 24; 39828ce5ce6Saurel32 399e1fe50dcSStefan Weil cpu_physical_memory_write(addr, &val, len); 40028ce5ce6Saurel32 40128ce5ce6Saurel32 if (conditional_wait(ch)) 402b42ec42dSaurel32 goto wait; 40328ce5ce6Saurel32 404ad674e53SAurelien Jarno current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); 40528ce5ce6Saurel32 dbdma_cmdptr_save(ch); 406ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~FLUSH; 40728ce5ce6Saurel32 40828ce5ce6Saurel32 conditional_interrupt(ch); 40928ce5ce6Saurel32 next(ch); 41028ce5ce6Saurel32 411b42ec42dSaurel32 wait: 412d2f0ce21SAlexander Graf DBDMA_kick(dbdma_from_ch(ch)); 41328ce5ce6Saurel32 } 41428ce5ce6Saurel32 415b42ec42dSaurel32 static void nop(DBDMA_channel *ch) 41628ce5ce6Saurel32 { 41728ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 41828ce5ce6Saurel32 41928ce5ce6Saurel32 if (conditional_wait(ch)) 420b42ec42dSaurel32 goto wait; 42128ce5ce6Saurel32 422ad674e53SAurelien Jarno current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); 42328ce5ce6Saurel32 dbdma_cmdptr_save(ch); 42428ce5ce6Saurel32 42528ce5ce6Saurel32 conditional_interrupt(ch); 42628ce5ce6Saurel32 conditional_branch(ch); 42728ce5ce6Saurel32 428b42ec42dSaurel32 wait: 429d2f0ce21SAlexander Graf DBDMA_kick(dbdma_from_ch(ch)); 43028ce5ce6Saurel32 } 43128ce5ce6Saurel32 432b42ec42dSaurel32 static void stop(DBDMA_channel *ch) 43328ce5ce6Saurel32 { 434ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH); 43528ce5ce6Saurel32 43628ce5ce6Saurel32 /* the stop command does not increment command pointer */ 43728ce5ce6Saurel32 } 43828ce5ce6Saurel32 439b42ec42dSaurel32 static void channel_run(DBDMA_channel *ch) 44028ce5ce6Saurel32 { 44128ce5ce6Saurel32 dbdma_cmd *current = &ch->current; 44228ce5ce6Saurel32 uint16_t cmd, key; 44328ce5ce6Saurel32 uint16_t req_count; 44428ce5ce6Saurel32 uint32_t phy_addr; 44528ce5ce6Saurel32 44628ce5ce6Saurel32 DBDMA_DPRINTF("channel_run\n"); 44728ce5ce6Saurel32 dump_dbdma_cmd(current); 44828ce5ce6Saurel32 44928ce5ce6Saurel32 /* clear WAKE flag at command fetch */ 45028ce5ce6Saurel32 451ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] &= ~WAKE; 45228ce5ce6Saurel32 45328ce5ce6Saurel32 cmd = le16_to_cpu(current->command) & COMMAND_MASK; 45428ce5ce6Saurel32 45528ce5ce6Saurel32 switch (cmd) { 45628ce5ce6Saurel32 case DBDMA_NOP: 457b42ec42dSaurel32 nop(ch); 458b42ec42dSaurel32 return; 45928ce5ce6Saurel32 46028ce5ce6Saurel32 case DBDMA_STOP: 461b42ec42dSaurel32 stop(ch); 462b42ec42dSaurel32 return; 46328ce5ce6Saurel32 } 46428ce5ce6Saurel32 46528ce5ce6Saurel32 key = le16_to_cpu(current->command) & 0x0700; 46628ce5ce6Saurel32 req_count = le16_to_cpu(current->req_count); 46728ce5ce6Saurel32 phy_addr = le32_to_cpu(current->phy_addr); 46828ce5ce6Saurel32 46928ce5ce6Saurel32 if (key == KEY_STREAM4) { 47028ce5ce6Saurel32 printf("command %x, invalid key 4\n", cmd); 47128ce5ce6Saurel32 kill_channel(ch); 472b42ec42dSaurel32 return; 47328ce5ce6Saurel32 } 47428ce5ce6Saurel32 47528ce5ce6Saurel32 switch (cmd) { 47628ce5ce6Saurel32 case OUTPUT_MORE: 477b42ec42dSaurel32 start_output(ch, key, phy_addr, req_count, 0); 478b42ec42dSaurel32 return; 47928ce5ce6Saurel32 48028ce5ce6Saurel32 case OUTPUT_LAST: 481b42ec42dSaurel32 start_output(ch, key, phy_addr, req_count, 1); 482b42ec42dSaurel32 return; 48328ce5ce6Saurel32 48428ce5ce6Saurel32 case INPUT_MORE: 485b42ec42dSaurel32 start_input(ch, key, phy_addr, req_count, 0); 486b42ec42dSaurel32 return; 48728ce5ce6Saurel32 48828ce5ce6Saurel32 case INPUT_LAST: 489b42ec42dSaurel32 start_input(ch, key, phy_addr, req_count, 1); 490b42ec42dSaurel32 return; 49128ce5ce6Saurel32 } 49228ce5ce6Saurel32 49328ce5ce6Saurel32 if (key < KEY_REGS) { 49428ce5ce6Saurel32 printf("command %x, invalid key %x\n", cmd, key); 49528ce5ce6Saurel32 key = KEY_SYSTEM; 49628ce5ce6Saurel32 } 49728ce5ce6Saurel32 49828ce5ce6Saurel32 /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits 49928ce5ce6Saurel32 * and BRANCH is invalid 50028ce5ce6Saurel32 */ 50128ce5ce6Saurel32 50228ce5ce6Saurel32 req_count = req_count & 0x0007; 50328ce5ce6Saurel32 if (req_count & 0x4) { 50428ce5ce6Saurel32 req_count = 4; 50528ce5ce6Saurel32 phy_addr &= ~3; 50628ce5ce6Saurel32 } else if (req_count & 0x2) { 50728ce5ce6Saurel32 req_count = 2; 50828ce5ce6Saurel32 phy_addr &= ~1; 50928ce5ce6Saurel32 } else 51028ce5ce6Saurel32 req_count = 1; 51128ce5ce6Saurel32 51228ce5ce6Saurel32 switch (cmd) { 51328ce5ce6Saurel32 case LOAD_WORD: 514b42ec42dSaurel32 load_word(ch, key, phy_addr, req_count); 515b42ec42dSaurel32 return; 51628ce5ce6Saurel32 51728ce5ce6Saurel32 case STORE_WORD: 518b42ec42dSaurel32 store_word(ch, key, phy_addr, req_count); 519b42ec42dSaurel32 return; 52028ce5ce6Saurel32 } 52128ce5ce6Saurel32 } 52228ce5ce6Saurel32 523c20df14bSJuan Quintela static void DBDMA_run(DBDMAState *s) 52428ce5ce6Saurel32 { 52528ce5ce6Saurel32 int channel; 52628ce5ce6Saurel32 527c20df14bSJuan Quintela for (channel = 0; channel < DBDMA_CHANNELS; channel++) { 528c20df14bSJuan Quintela DBDMA_channel *ch = &s->channels[channel]; 529ad674e53SAurelien Jarno uint32_t status = ch->regs[DBDMA_STATUS]; 53003ee3b1eSAlexander Graf if (!ch->io.processing && (status & RUN) && (status & ACTIVE)) { 531b42ec42dSaurel32 channel_run(ch); 53228ce5ce6Saurel32 } 53328ce5ce6Saurel32 } 534c20df14bSJuan Quintela } 53528ce5ce6Saurel32 53628ce5ce6Saurel32 static void DBDMA_run_bh(void *opaque) 53728ce5ce6Saurel32 { 538c20df14bSJuan Quintela DBDMAState *s = opaque; 53928ce5ce6Saurel32 54028ce5ce6Saurel32 DBDMA_DPRINTF("DBDMA_run_bh\n"); 54128ce5ce6Saurel32 542c20df14bSJuan Quintela DBDMA_run(s); 54328ce5ce6Saurel32 } 54428ce5ce6Saurel32 545d1e562deSAlexander Graf void DBDMA_kick(DBDMAState *dbdma) 546d1e562deSAlexander Graf { 547d2f0ce21SAlexander Graf qemu_bh_schedule(dbdma->bh); 548d1e562deSAlexander Graf } 549d1e562deSAlexander Graf 55028ce5ce6Saurel32 void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, 551862c9280Saurel32 DBDMA_rw rw, DBDMA_flush flush, 55228ce5ce6Saurel32 void *opaque) 55328ce5ce6Saurel32 { 554c20df14bSJuan Quintela DBDMAState *s = dbdma; 555c20df14bSJuan Quintela DBDMA_channel *ch = &s->channels[nchan]; 55628ce5ce6Saurel32 55728ce5ce6Saurel32 DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan); 55828ce5ce6Saurel32 55928ce5ce6Saurel32 ch->irq = irq; 56028ce5ce6Saurel32 ch->channel = nchan; 561b42ec42dSaurel32 ch->rw = rw; 562862c9280Saurel32 ch->flush = flush; 56328ce5ce6Saurel32 ch->io.opaque = opaque; 56428ce5ce6Saurel32 ch->io.channel = ch; 56528ce5ce6Saurel32 } 56628ce5ce6Saurel32 56728ce5ce6Saurel32 static void 56828ce5ce6Saurel32 dbdma_control_write(DBDMA_channel *ch) 56928ce5ce6Saurel32 { 57028ce5ce6Saurel32 uint16_t mask, value; 57128ce5ce6Saurel32 uint32_t status; 57228ce5ce6Saurel32 573ad674e53SAurelien Jarno mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff; 574ad674e53SAurelien Jarno value = ch->regs[DBDMA_CONTROL] & 0xffff; 57528ce5ce6Saurel32 57628ce5ce6Saurel32 value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT); 57728ce5ce6Saurel32 578ad674e53SAurelien Jarno status = ch->regs[DBDMA_STATUS]; 57928ce5ce6Saurel32 58028ce5ce6Saurel32 status = (value & mask) | (status & ~mask); 58128ce5ce6Saurel32 58228ce5ce6Saurel32 if (status & WAKE) 58328ce5ce6Saurel32 status |= ACTIVE; 58428ce5ce6Saurel32 if (status & RUN) { 58528ce5ce6Saurel32 status |= ACTIVE; 58628ce5ce6Saurel32 status &= ~DEAD; 58728ce5ce6Saurel32 } 58828ce5ce6Saurel32 if (status & PAUSE) 58928ce5ce6Saurel32 status &= ~ACTIVE; 590ad674e53SAurelien Jarno if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) { 59128ce5ce6Saurel32 /* RUN is cleared */ 59228ce5ce6Saurel32 status &= ~(ACTIVE|DEAD); 593987422bcSAmadeusz Sławiński if ((status & FLUSH) && ch->flush) { 594987422bcSAmadeusz Sławiński ch->flush(&ch->io); 595987422bcSAmadeusz Sławiński status &= ~FLUSH; 596987422bcSAmadeusz Sławiński } 59728ce5ce6Saurel32 } 59828ce5ce6Saurel32 59928ce5ce6Saurel32 DBDMA_DPRINTF(" status 0x%08x\n", status); 60028ce5ce6Saurel32 601ad674e53SAurelien Jarno ch->regs[DBDMA_STATUS] = status; 60228ce5ce6Saurel32 603d2f0ce21SAlexander Graf if (status & ACTIVE) { 604d2f0ce21SAlexander Graf DBDMA_kick(dbdma_from_ch(ch)); 605d2f0ce21SAlexander Graf } 606d2f0ce21SAlexander Graf if ((status & FLUSH) && ch->flush) { 607862c9280Saurel32 ch->flush(&ch->io); 6083cbee15bSj_mayer } 609d2f0ce21SAlexander Graf } 6103cbee15bSj_mayer 611a8170e5eSAvi Kivity static void dbdma_write(void *opaque, hwaddr addr, 61223c5e4caSAvi Kivity uint64_t value, unsigned size) 6133cbee15bSj_mayer { 61428ce5ce6Saurel32 int channel = addr >> DBDMA_CHANNEL_SHIFT; 615c20df14bSJuan Quintela DBDMAState *s = opaque; 616c20df14bSJuan Quintela DBDMA_channel *ch = &s->channels[channel]; 61728ce5ce6Saurel32 int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; 61828ce5ce6Saurel32 61958c0c311SAlexander Graf DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", 62058c0c311SAlexander Graf addr, value); 62128ce5ce6Saurel32 DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", 62228ce5ce6Saurel32 (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); 62328ce5ce6Saurel32 6247eaba824SAlexander Graf /* cmdptr cannot be modified if channel is ACTIVE */ 62528ce5ce6Saurel32 6267eaba824SAlexander Graf if (reg == DBDMA_CMDPTR_LO && (ch->regs[DBDMA_STATUS] & ACTIVE)) { 62728ce5ce6Saurel32 return; 6287eaba824SAlexander Graf } 62928ce5ce6Saurel32 63028ce5ce6Saurel32 ch->regs[reg] = value; 63128ce5ce6Saurel32 63228ce5ce6Saurel32 switch(reg) { 63328ce5ce6Saurel32 case DBDMA_CONTROL: 63428ce5ce6Saurel32 dbdma_control_write(ch); 63528ce5ce6Saurel32 break; 63628ce5ce6Saurel32 case DBDMA_CMDPTR_LO: 63728ce5ce6Saurel32 /* 16-byte aligned */ 638ad674e53SAurelien Jarno ch->regs[DBDMA_CMDPTR_LO] &= ~0xf; 63928ce5ce6Saurel32 dbdma_cmdptr_load(ch); 64028ce5ce6Saurel32 break; 64128ce5ce6Saurel32 case DBDMA_STATUS: 64228ce5ce6Saurel32 case DBDMA_INTR_SEL: 64328ce5ce6Saurel32 case DBDMA_BRANCH_SEL: 64428ce5ce6Saurel32 case DBDMA_WAIT_SEL: 64528ce5ce6Saurel32 /* nothing to do */ 64628ce5ce6Saurel32 break; 64728ce5ce6Saurel32 case DBDMA_XFER_MODE: 64828ce5ce6Saurel32 case DBDMA_CMDPTR_HI: 64928ce5ce6Saurel32 case DBDMA_DATA2PTR_HI: 65028ce5ce6Saurel32 case DBDMA_DATA2PTR_LO: 65128ce5ce6Saurel32 case DBDMA_ADDRESS_HI: 65228ce5ce6Saurel32 case DBDMA_BRANCH_ADDR_HI: 65328ce5ce6Saurel32 case DBDMA_RES1: 65428ce5ce6Saurel32 case DBDMA_RES2: 65528ce5ce6Saurel32 case DBDMA_RES3: 65628ce5ce6Saurel32 case DBDMA_RES4: 65728ce5ce6Saurel32 /* unused */ 65828ce5ce6Saurel32 break; 6593cbee15bSj_mayer } 6603cbee15bSj_mayer } 6613cbee15bSj_mayer 662a8170e5eSAvi Kivity static uint64_t dbdma_read(void *opaque, hwaddr addr, 66323c5e4caSAvi Kivity unsigned size) 6643cbee15bSj_mayer { 66528ce5ce6Saurel32 uint32_t value; 66628ce5ce6Saurel32 int channel = addr >> DBDMA_CHANNEL_SHIFT; 667c20df14bSJuan Quintela DBDMAState *s = opaque; 668c20df14bSJuan Quintela DBDMA_channel *ch = &s->channels[channel]; 66928ce5ce6Saurel32 int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; 670ea026b2fSblueswir1 67128ce5ce6Saurel32 value = ch->regs[reg]; 67228ce5ce6Saurel32 67328ce5ce6Saurel32 DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value); 67428ce5ce6Saurel32 DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", 67528ce5ce6Saurel32 (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); 67628ce5ce6Saurel32 67728ce5ce6Saurel32 switch(reg) { 67828ce5ce6Saurel32 case DBDMA_CONTROL: 67928ce5ce6Saurel32 value = 0; 68028ce5ce6Saurel32 break; 68128ce5ce6Saurel32 case DBDMA_STATUS: 68228ce5ce6Saurel32 case DBDMA_CMDPTR_LO: 68328ce5ce6Saurel32 case DBDMA_INTR_SEL: 68428ce5ce6Saurel32 case DBDMA_BRANCH_SEL: 68528ce5ce6Saurel32 case DBDMA_WAIT_SEL: 68628ce5ce6Saurel32 /* nothing to do */ 68728ce5ce6Saurel32 break; 68828ce5ce6Saurel32 case DBDMA_XFER_MODE: 68928ce5ce6Saurel32 case DBDMA_CMDPTR_HI: 69028ce5ce6Saurel32 case DBDMA_DATA2PTR_HI: 69128ce5ce6Saurel32 case DBDMA_DATA2PTR_LO: 69228ce5ce6Saurel32 case DBDMA_ADDRESS_HI: 69328ce5ce6Saurel32 case DBDMA_BRANCH_ADDR_HI: 69428ce5ce6Saurel32 /* unused */ 69528ce5ce6Saurel32 value = 0; 69628ce5ce6Saurel32 break; 69728ce5ce6Saurel32 case DBDMA_RES1: 69828ce5ce6Saurel32 case DBDMA_RES2: 69928ce5ce6Saurel32 case DBDMA_RES3: 70028ce5ce6Saurel32 case DBDMA_RES4: 70128ce5ce6Saurel32 /* reserved */ 70228ce5ce6Saurel32 break; 70328ce5ce6Saurel32 } 70428ce5ce6Saurel32 70528ce5ce6Saurel32 return value; 7063cbee15bSj_mayer } 7073cbee15bSj_mayer 70823c5e4caSAvi Kivity static const MemoryRegionOps dbdma_ops = { 70923c5e4caSAvi Kivity .read = dbdma_read, 71023c5e4caSAvi Kivity .write = dbdma_write, 71123c5e4caSAvi Kivity .endianness = DEVICE_LITTLE_ENDIAN, 71223c5e4caSAvi Kivity .valid = { 71323c5e4caSAvi Kivity .min_access_size = 4, 71423c5e4caSAvi Kivity .max_access_size = 4, 71523c5e4caSAvi Kivity }, 7163cbee15bSj_mayer }; 7173cbee15bSj_mayer 718da26fdc3SJuan Quintela static const VMStateDescription vmstate_dbdma_channel = { 719da26fdc3SJuan Quintela .name = "dbdma_channel", 720da26fdc3SJuan Quintela .version_id = 0, 721da26fdc3SJuan Quintela .minimum_version_id = 0, 722da26fdc3SJuan Quintela .fields = (VMStateField[]) { 723da26fdc3SJuan Quintela VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS), 724da26fdc3SJuan Quintela VMSTATE_END_OF_LIST() 7259b64997fSblueswir1 } 726da26fdc3SJuan Quintela }; 7279b64997fSblueswir1 728da26fdc3SJuan Quintela static const VMStateDescription vmstate_dbdma = { 729da26fdc3SJuan Quintela .name = "dbdma", 730da26fdc3SJuan Quintela .version_id = 2, 731da26fdc3SJuan Quintela .minimum_version_id = 2, 732da26fdc3SJuan Quintela .fields = (VMStateField[]) { 733da26fdc3SJuan Quintela VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1, 734da26fdc3SJuan Quintela vmstate_dbdma_channel, DBDMA_channel), 735da26fdc3SJuan Quintela VMSTATE_END_OF_LIST() 7369b64997fSblueswir1 } 737da26fdc3SJuan Quintela }; 7389b64997fSblueswir1 7396e6b7363Sblueswir1 static void dbdma_reset(void *opaque) 7406e6b7363Sblueswir1 { 741c20df14bSJuan Quintela DBDMAState *s = opaque; 74228ce5ce6Saurel32 int i; 74328ce5ce6Saurel32 74428ce5ce6Saurel32 for (i = 0; i < DBDMA_CHANNELS; i++) 745c20df14bSJuan Quintela memset(s->channels[i].regs, 0, DBDMA_SIZE); 7466e6b7363Sblueswir1 } 7476e6b7363Sblueswir1 74823c5e4caSAvi Kivity void* DBDMA_init (MemoryRegion **dbdma_mem) 7493cbee15bSj_mayer { 750c20df14bSJuan Quintela DBDMAState *s; 751*3e300fa6SAlexander Graf int i; 75228ce5ce6Saurel32 7537267c094SAnthony Liguori s = g_malloc0(sizeof(DBDMAState)); 75428ce5ce6Saurel32 755*3e300fa6SAlexander Graf for (i = 0; i < DBDMA_CHANNELS; i++) { 756*3e300fa6SAlexander Graf DBDMA_io *io = &s->channels[i].io; 757*3e300fa6SAlexander Graf qemu_iovec_init(&io->iov, 1); 758*3e300fa6SAlexander Graf } 759*3e300fa6SAlexander Graf 7602c9b15caSPaolo Bonzini memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000); 76123c5e4caSAvi Kivity *dbdma_mem = &s->mem; 762da26fdc3SJuan Quintela vmstate_register(NULL, -1, &vmstate_dbdma, s); 763a08d4367SJan Kiszka qemu_register_reset(dbdma_reset, s); 76428ce5ce6Saurel32 765d2f0ce21SAlexander Graf s->bh = qemu_bh_new(DBDMA_run_bh, s); 76628ce5ce6Saurel32 76728ce5ce6Saurel32 return s; 7683cbee15bSj_mayer } 769