/* Commit and/or discard all DMA descriptors and buffers pointed by them, * handle circular lists. At the same time, convert virtual pointers to * real ones */ static void dma_commit_and_discard(unsigned chan, struct apb_dma_command_t *cmd) { /* We handle circular descriptors by using unused bits: * bits 8-11 are not used by the hardware so we first go through the whole * list and mark them all a special value at the same time we commit buffers * and then we go through the list another time to clear the mark and * commit the descriptors */ struct apb_dma_command_t *cur = cmd; while((cur->cmd & HW_APB_CHx_CMD__UNUSED_BM) != HW_APB_CHx_CMD__UNUSED_MAGIC) { cur->cmd = (cur->cmd & ~HW_APB_CHx_CMD__UNUSED_BM) | HW_APB_CHx_CMD__UNUSED_MAGIC; int op = cur->cmd & HW_APB_CHx_CMD__COMMAND_BM; int sz = __XTRACT_EX(cur->cmd, HW_APB_CHx_CMD__XFER_COUNT); /* device > host: discard */ if(op == HW_APB_CHx_CMD__COMMAND__WRITE) discard_dcache_range(cur->buffer, sz); /* host > device: commit and discard */ else if(op == HW_APB_CHx_CMD__COMMAND__READ) commit_discard_dcache_range(cur->buffer, sz); if((uint32_t)cur->buffer % CACHEALIGN_SIZE) apb_nr_unaligned[chan]++; /* Virtual to physical buffer pointer conversion */ cur->buffer = PHYSICAL_ADDR(cur->buffer); /* chain ? */ if(cur->cmd & HW_APB_CHx_CMD__CHAIN) cur = cur->next; else break; } cur = cmd; while((cur->cmd & HW_APB_CHx_CMD__UNUSED_BM) != 0) { cur->cmd = cur->cmd & ~HW_APB_CHx_CMD__UNUSED_BM; int sz = __XTRACT_EX(cur->cmd, HW_APB_CHx_CMD__CMDWORDS) * sizeof(uint32_t); /* commit descriptor and discard descriptor */ /* chain ? */ if(cur->cmd & HW_APB_CHx_CMD__CHAIN) { struct apb_dma_command_t *next = cur->next; cur->next = PHYSICAL_ADDR(cur->next); commit_dcache_range(cur, sizeof(struct apb_dma_command_t) + sz); cur = next; } else { commit_dcache_range(cur, sizeof(struct apb_dma_command_t) + sz); break; } } }
void dma_start_command(unsigned chan, struct apb_dma_command_t *cmd) { dma_commit_and_discard(chan, cmd); if(APB_IS_APBX_CHANNEL(chan)) { HW_APBX_CHx_NXTCMDAR(APB_GET_DMA_CHANNEL(chan)) = (uint32_t)PHYSICAL_ADDR(cmd); HW_APBX_CHx_SEMA(APB_GET_DMA_CHANNEL(chan)) = 1; } else { HW_APBH_CHx_NXTCMDAR(APB_GET_DMA_CHANNEL(chan)) = (uint32_t)PHYSICAL_ADDR(cmd); HW_APBH_CHx_SEMA(APB_GET_DMA_CHANNEL(chan)) = 1; } }
static void ep_transfer(int ep, void *ptr, int len, bool out) { /* disable interrupts to avoid any race */ int oldlevel = disable_irq_save(); struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN]; endpoint->busy = true; endpoint->size = len; endpoint->status = -1; if (out) DEPCTL(ep, out) &= ~DEPCTL_stall; int mps = usb_drv_port_speed() ? 512 : 64; int nb_packets = (len + mps - 1) / mps; if (nb_packets == 0) nb_packets = 1; DEPDMA(ep, out) = len ? (void*)PHYSICAL_ADDR(ptr) : NULL; DEPTSIZ(ep, out) = (nb_packets << DEPTSIZ_pkcnt_bitp) | len; if(out) discard_dcache_range(ptr, len); else commit_dcache_range(ptr, len); logf("pkt=%d dma=%lx", nb_packets, DEPDMA(ep, out)); // if (!out) while (((GNPTXSTS & 0xffff) << 2) < MIN(mps, length)); DEPCTL(ep, out) |= DEPCTL_epena | DEPCTL_cnak; restore_irq(oldlevel); }
static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) { uint32_t delta, next; uint32_t addr = (uint32_t)buf; size_t rsz = roundup(sz, 32); int idx; if (sz != rsz) debug("EHCI-HCD: Misaligned buffer size (%08x)\n", sz); if (addr & 31) debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf); idx = 0; while (idx < 5) { flush_dcache_range(addr, addr + rsz); td->qt_buffer[idx] = cpu_to_hc32(PHYSICAL_ADDR(addr)); td->qt_buffer_hi[idx] = 0; next = (addr + 4096) & ~4095; delta = next - addr; if (delta >= sz) break; sz -= delta; addr = next; idx++; } if (idx == 5) { debug("out of buffer pointers (%u bytes left)\n", sz); return -1; } return 0; }
static enum imx233_dcp_error_t imx233_dcp_job(int ch) { /* if IRQs are not enabled, don't enable channel interrupt and do some polling */ bool irq_enabled = irq_enabled(); /* enable channel, clear interrupt, enable interrupt */ imx233_icoll_enable_interrupt(INT_SRC_DCP, true); if(irq_enabled) __REG_SET(HW_DCP_CTRL) = HW_DCP_CTRL__CHANNEL_INTERRUPT_ENABLE(ch); __REG_CLR(HW_DCP_STAT) = HW_DCP_STAT__IRQ(ch); __REG_SET(HW_DCP_CHANNELCTRL) = HW_DCP_CHANNELCTRL__ENABLE_CHANNEL(ch); /* write back packet */ commit_discard_dcache_range(&channel_packet[ch], sizeof(struct imx233_dcp_packet_t)); /* write 1 to semaphore to run job */ HW_DCP_CHxCMDPTR(ch) = (uint32_t)PHYSICAL_ADDR(&channel_packet[ch]); HW_DCP_CHxSEMA(ch) = 1; /* wait completion */ if(irq_enabled) semaphore_wait(&channel_sema[ch], TIMEOUT_BLOCK); else while(__XTRACT_EX(HW_DCP_CHxSEMA(ch), HW_DCP_CHxSEMA__VALUE)) udelay(10); /* disable channel and interrupt */ __REG_CLR(HW_DCP_CTRL) = HW_DCP_CTRL__CHANNEL_INTERRUPT_ENABLE(ch); __REG_CLR(HW_DCP_CHANNELCTRL) = HW_DCP_CHANNELCTRL__ENABLE_CHANNEL(ch); /* read status */ return get_error_status(ch); }
// Handle select DOS interrupt 21h services. void Int21h(int intNum) { char *string; unsigned int i; unsigned int maxi; int length; switch(M.x86.R_AH) { case 0x02: // Character output. DL is 8-bit character to print ProgramWindow->PrintChar(M.x86.R_DL); break; case 0x09: // String output. DS:DX points to '$' terminated string string = (char *)M.privatevp; i = PHYSICAL_ADDR(M.x86.R_DS, M.x86.R_DX); for(maxi = i; string[maxi] != '$' && maxi < M.mem_size; maxi++); length = maxi - i; if(length > 0) ProgramWindow->SendString(string + i, length); break; case 0x40: // Write to device or file. Only supports deivce 0x01 (stdout). BX is handle, CX is num bytes, DS:DX points to data to write. if(M.x86.R_BX != 1) { ProgramWindow->PrintText("ERROR: Only handle 1 (stdout) supported for INT 21h, service 40h. Handle %Xh was requested.\n", M.x86.R_BX); break; } i = PHYSICAL_ADDR(M.x86.R_DS, M.x86.R_DX); length = M.x86.R_CX; if(i + length >= M.mem_size) length = M.mem_size - i; if(length > 0) ProgramWindow->SendString((char *)M.privatevp + i, length); break; case 0x4C: // Terminate with return code. AL is return code SimControl->ProgramTerminated((int)(M.x86.R_AL)); break; default: ProgramWindow->PrintText("ERROR: INT 21h, service %xh not supported.\n", (unsigned)M.x86.R_AH); } }
static void prepare_setup_ep0(void) { DEPDMA(0, true) = (void*)PHYSICAL_ADDR(&_ep0_setup_pkt); DEPTSIZ(0, true) = (1 << DEPTSIZ0_supcnt_bitp) | (1 << DEPTSIZ0_pkcnt_bitp) | 8; DEPCTL(0, true) |= DEPCTL_epena | DEPCTL_cnak; ep0_state = EP0_WAIT_SETUP; }
enum imx233_dcp_error_t imx233_dcp_blit_ex(int ch, bool fill, const void *src, size_t w, size_t h, void *dst, size_t out_w) { /* prepare packet */ channel_packet[ch].next = 0; channel_packet[ch].ctrl0 = HW_DCP_CTRL0__INTERRUPT_ENABLE | HW_DCP_CTRL0__ENABLE_MEMCOPY | HW_DCP_CTRL0__DECR_SEMAPHORE | HW_DCP_CTRL0__ENABLE_BLIT | (fill ? HW_DCP_CTRL0__CONSTANT_FILL : 0); channel_packet[ch].ctrl1 = out_w; channel_packet[ch].src = (uint32_t)(fill ? src : PHYSICAL_ADDR(src)); channel_packet[ch].dst = (uint32_t)PHYSICAL_ADDR(dst); channel_packet[ch].size = w | h << HW_DCP_SIZE__NUMBER_LINES_BP; channel_packet[ch].payload = 0; channel_packet[ch].status = 0; /* we have a problem here to discard the output buffer since it's not contiguous * so only commit the source */ if(!fill) commit_discard_dcache_range(src, w * h); /* do the job */ return imx233_dcp_job(ch); }
enum imx233_dcp_error_t imx233_dcp_memcpy_ex(int ch, bool fill, const void *src, void *dst, size_t len) { /* prepare packet */ channel_packet[ch].next = 0; channel_packet[ch].ctrl0 = HW_DCP_CTRL0__INTERRUPT_ENABLE | HW_DCP_CTRL0__ENABLE_MEMCOPY | HW_DCP_CTRL0__DECR_SEMAPHORE | (fill ? HW_DCP_CTRL0__CONSTANT_FILL : 0); channel_packet[ch].ctrl1 = 0; channel_packet[ch].src = (uint32_t)(fill ? src : PHYSICAL_ADDR(src)); channel_packet[ch].dst = (uint32_t)PHYSICAL_ADDR(dst); channel_packet[ch].size = len; channel_packet[ch].payload = 0; channel_packet[ch].status = 0; /* write-back src if not filling, discard dst */ if(!fill) commit_discard_dcache_range(src, len); discard_dcache_range(dst, len); /* do the job */ return imx233_dcp_job(ch); }
void imx233_dcp_init(void) { /* Reset block */ imx233_reset_block(&HW_DCP_CTRL); /* Setup contexte pointer */ HW_DCP_CONTEXT = (uint32_t)PHYSICAL_ADDR(&dcp_context); /* Enable context switching and caching */ __REG_SET(HW_DCP_CTRL) = HW_DCP_CTRL__ENABLE_CONTEXT_CACHING | HW_DCP_CTRL__ENABLE_CONTEXT_SWITCHING; /* Check that there are sufficiently many channels */ if(__XTRACT(HW_DCP_CAPABILITY0, NUM_CHANNELS) != HW_DCP_NUM_CHANNELS) panicf("DCP has %lu channels but was configured to use %d !", __XTRACT(HW_DCP_CAPABILITY0, NUM_CHANNELS), HW_DCP_NUM_CHANNELS); /* Setup channel arbiter to use */ arbiter_init(&channel_arbiter, HW_DCP_NUM_CHANNELS); /* Merge channel0 interrupt */ __REG_SET(HW_DCP_CHANNELCTRL) = HW_DCP_CHANNELCTRL__CH0_IRQ_MERGED; /* setup semaphores */ for(int i = 0; i< HW_DCP_NUM_CHANNELS; i++) semaphore_init(&channel_sema[i], 1, 0); }
// Takes a string representing an Address // It can be of the following forms: // 1) seg:off (hex only for segment and offset) // 2) 0xAB, 0XAB (hex) // 3) ABh, ABH (hex) // 3) 171 (decimal assumed) void InterpretAddress(char *addrString, Address *addr) { int i, len; bool isColon; char *segmentStr, *offsetStr; isColon = false; // Determin string length for(len = 0; addrString[len] != '\0'; len++); // Check for HEX segment offset form 0000:0000 for(i = 0; i < len; i++) { if(addrString[i] == ':') { isColon = true; addrString[i] = '\0'; // Null terminate segment segmentStr = &addrString[0]; offsetStr = &addrString[i+1]; } } if(isColon) { // Of form seg:off. Get the two and calculate physical address. //addr->segment = XtoI(segmentStr); //addr->offset = XtoI(offsetStr); sscanf(segmentStr, "%x", &(addr->segment)); sscanf(offsetStr, "%x", &(addr->offset)); addr->physical = PHYSICAL_ADDR(addr->segment, addr->offset); } else { // Single number. Get it and calculate segment and offset. addr->physical = InterpretNumber(addrString); addr->segment = SEGMENT(addr->physical); addr->offset = OFFSET(addr->physical); } }
static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { static struct QH qh __attribute__((aligned(32))); static struct qTD qtd[3] __attribute__((aligned (32))); int qtd_counter = 0; volatile struct qTD *vtd; unsigned long ts; uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; int timeout; int ret = 0; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); if (req != NULL) debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index)); memset(&qh, 0, sizeof(struct QH)); memset(qtd, 0, sizeof(qtd)); toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); /* * Setup QH (3.6 in ehci-r10.pdf) * * qh_link ................. 03-00 H * qh_endpt1 ............... 07-04 H * qh_endpt2 ............... 0B-08 H * - qh_curtd * qh_overlay.qt_next ...... 13-10 H * - qh_overlay.qt_altnext */ qh.qh_link = cpu_to_hc32((uint32_t)PHYSICAL_ADDR(&qh_list) | QH_LINK_TYPE_QH); c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; endpt = (8 << 28) | (c << 27) | (usb_maxpacket(dev, pipe) << 16) | (0 << 15) | (1 << 14) | (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); qh.qh_endpt1 = cpu_to_hc32(endpt); endpt = (1 << 30) | (dev->portnr << 23) | (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); qh.qh_endpt2 = cpu_to_hc32(endpt); qh.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); tdp = &qh.qh_overlay.qt_next; if (req != NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H * * [ buffer, buffer_hi ] loaded with "req". */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (0 << 31) | (sizeof(*req) << 16) | (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); qtd[qtd_counter].qt_token = cpu_to_hc32(token); if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) { debug("unable construct SETUP td\n"); goto fail; } /* Update previous qTD! */ *tdp = cpu_to_hc32((uint32_t)PHYSICAL_ADDR(&qtd[qtd_counter])); tdp = &qtd[qtd_counter++].qt_next; toggle = 1; } if (length > 0 || req == NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H * * [ buffer, buffer_hi ] loaded with "buffer". */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (length << 16) | ((req == NULL ? 1 : 0) << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); qtd[qtd_counter].qt_token = cpu_to_hc32(token); if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) { debug("unable construct DATA td\n"); goto fail; } /* Update previous qTD! */ *tdp = cpu_to_hc32((uint32_t)PHYSICAL_ADDR(&qtd[qtd_counter])); tdp = &qtd[qtd_counter++].qt_next; } if (req != NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (0 << 16) | (1 << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); qtd[qtd_counter].qt_token = cpu_to_hc32(token); /* Update previous qTD! */ *tdp = cpu_to_hc32((uint32_t)PHYSICAL_ADDR(&qtd[qtd_counter])); tdp = &qtd[qtd_counter++].qt_next; } qh_list.qh_link = cpu_to_hc32((uint32_t) PHYSICAL_ADDR(&qh) | QH_LINK_TYPE_QH); /* Flush dcache */ flush_dcache_range((uint32_t)&qh_list, (uint32_t)&qh_list + sizeof(struct QH)); flush_dcache_range((uint32_t)&qh, (uint32_t)&qh + sizeof(struct QH)); flush_dcache_range((uint32_t)qtd, (uint32_t)qtd + sizeof(qtd)); usbsts = ehci_readl(&hcor->or_usbsts); ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STD_ASS set\n"); goto fail; } /* Wait for TDs to be processed. */ ts = get_timer(0); vtd = &qtd[qtd_counter - 1]; timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ invalidate_dcache_range((uint32_t)&qh_list, (uint32_t)&qh_list + sizeof(struct QH)); invalidate_dcache_range((uint32_t)&qh, (uint32_t)&qh + sizeof(struct QH)); invalidate_dcache_range((uint32_t)qtd, (uint32_t)qtd + sizeof(qtd)); token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); /* Invalidate the memory area occupied by buffer */ if (buffer!=NULL) { invalidate_dcache_range(((uint32_t)buffer & ~31), ((uint32_t)buffer & ~31) + roundup(length, 32)); } /* Check that the TD processing happened */ if (token & 0x80) { printf("EHCI timed out on TD - token=%#x\n", token); } /* Disable async schedule. */ cmd = ehci_readl(&hcor->or_usbcmd); cmd &= ~CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STD_ASS reset\n"); goto fail; } qh_list.qh_link = cpu_to_hc32((uint32_t)PHYSICAL_ADDR(&qh_list) | QH_LINK_TYPE_QH); token = hc32_to_cpu(qh.qh_overlay.qt_token); if (!(token & 0x80)) { debug("TOKEN=%#x\n", token); switch (token & 0xfc) { case 0: toggle = token >> 31; usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); dev->status = 0; break; case 0x40: dev->status = USB_ST_STALLED; break; case 0xa0: case 0x20: dev->status = USB_ST_BUF_ERR; break; case 0x50: case 0x10: dev->status = USB_ST_BABBLE_DET; break; default: dev->status = USB_ST_CRC_ERR; if ((token & 0x40) == 0x40) dev->status |= USB_ST_STALLED; break; } dev->act_len = length - ((token >> 16) & 0x7fff); } else {