static int ps2_recvbyte(int aux, int needack, int timeout) { u64 end = calc_future_tsc(timeout); for (;;) { u8 status = inb(PORT_PS2_STATUS); if (status & I8042_STR_OBF) { u8 data = inb(PORT_PS2_DATA); dprintf(7, "ps2 read %x\n", data); if (!!(status & I8042_STR_AUXDATA) == aux) { if (!needack) return data; if (data == PS2_RET_ACK) return data; if (data == PS2_RET_NAK) { dprintf(1, "Got ps2 nak (status=%x)\n", status); return data; } } // This data not part of command - just discard it. dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); } if (check_tsc(end)) { // Don't warn on second byte of a reset if (timeout > 100) warn_timeout(); return -1; } yield(); } }
static int ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) { u64 end = calc_future_tsc(timeout); u32 status; for (;;) { status = td->token; if (!(status & QTD_STS_ACTIVE)) break; if (check_tsc(end)) { u32 cur = GET_LOWFLAT(pipe->qh.current); u32 tok = GET_LOWFLAT(pipe->qh.token); u32 next = GET_LOWFLAT(pipe->qh.qtd_next); warn_timeout(); dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n" , pipe, cur, tok, next, td, status); ehci_reset_pipe(pipe); struct usb_ehci_s *cntl = container_of( GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb); ehci_waittick(cntl); return -1; } yield(); } if (status & QTD_STS_HALT) { dprintf(1, "ehci_wait_td error - status=%x\n", status); ehci_reset_pipe(pipe); return -2; } return 0; }
// Wait for next USB async frame to start - for ensuring safe memory release. static void ehci_waittick(struct usb_ehci_s *cntl) { if (MODE16) { msleep(10); return; } // Wait for access to "doorbell" barrier(); u32 cmd, sts; u64 end = calc_future_tsc(100); for (;;) { sts = readl(&cntl->regs->usbsts); if (!(sts & STS_IAA)) { cmd = readl(&cntl->regs->usbcmd); if (!(cmd & CMD_IAAD)) break; } if (check_tsc(end)) { warn_timeout(); return; } yield(); } // Ring "doorbell" writel(&cntl->regs->usbcmd, cmd | CMD_IAAD); // Wait for completion for (;;) { sts = readl(&cntl->regs->usbsts); if (sts & STS_IAA) break; if (check_tsc(end)) { warn_timeout(); return; } yield(); } // Ack completion writel(&cntl->regs->usbsts, STS_IAA); }
// Reset a drive static void ata_reset(struct atadrive_s *adrive_gf) { struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); u8 slave = GET_GLOBALFLAT(adrive_gf->slave); u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); dprintf(6, "ata_reset drive=%p\n", &adrive_gf->drive); // Pulse SRST outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC); udelay(5); outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC); msleep(2); // wait for device to become not busy. int status = await_not_bsy(iobase1); if (status < 0) goto done; if (slave) { // Change device. u32 end = timer_calc(IDE_TIMEOUT); for (;;) { outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); status = ndelay_await_not_bsy(iobase1); if (status < 0) goto done; if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) break; // Change drive request failed to take effect - retry. if (timer_check(end)) { warn_timeout(); goto done; } } } else { // QEMU doesn't reset dh on reset, so set it explicitly. outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH); } // On a user-reset request, wait for RDY if it is an ATA device. u8 type=GET_GLOBALFLAT(adrive_gf->drive.type); if (type == DTYPE_ATA) status = await_rdy(iobase1); done: // Enable interrupts outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); dprintf(6, "ata_reset exit status=%x\n", status); }
static int i8042_wait_write(void) { dprintf(7, "i8042_wait_write\n"); int i; for (i=0; i<I8042_CTL_TIMEOUT; i++) { u8 status = inb(PORT_PS2_STATUS); if (! (status & I8042_STR_IBF)) return 0; udelay(50); } warn_timeout(); return -1; }
// Wait for the specified ide state static inline int await_ide(u8 mask, u8 flags, u16 base, u16 timeout) { u32 end = timer_calc(timeout); for (;;) { u8 status = inb(base+ATA_CB_STAT); if ((status & mask) == flags) return status; if (timer_check(end)) { warn_timeout(); return -1; } yield(); } }
static int i8042_flush(void) { dprintf(7, "i8042_flush\n"); int i; for (i=0; i<I8042_BUFFER_SIZE; i++) { u8 status = inb(PORT_PS2_STATUS); if (! (status & I8042_STR_OBF)) return 0; udelay(50); inb(PORT_PS2_DATA); } warn_timeout(); return -1; }
/** * enter_state - Do common work of entering low-power state. * @state: pm_state structure for state we're entering. * * Make sure we're the only ones trying to enter a sleep state. Fail * if someone has beat us to it, since we don't want anything weird to * happen when we wake up. * Then, do the setup for suspend, enter the state, and cleaup (after * we've woken up). */ int enter_state(suspend_state_t state) { int error; if (!valid_state(state)) return -ENODEV; if (!mutex_trylock(&pm_mutex)) return -EBUSY; resume_from_deep_suspend = 0; printk(KERN_INFO "PM: Syncing filesystems ... "); sys_sync(); printk("done.\n"); pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); warn_timeout(SUSPEND_PREPARE_TIMEOUT, error = suspend_prepare() ); if (error) goto Unlock; if (suspend_test(TEST_FREEZER)) goto Finish; pr_debug("PM: Entering %s sleep\n", pm_states[state]); pm_restrict_gfp_mask(); error = suspend_devices_and_enter(state); pm_restore_gfp_mask(); Finish: pr_debug("PM: Finishing wakeup.\n"); suspend_finish(); Unlock: mutex_unlock(&pm_mutex); return error; }
// Reset device on port static int usb_hub_reset(struct usbhub_s *hub, u32 port) { int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); if (ret) goto fail; // Wait for reset to complete. struct usb_port_status sts; u64 end = calc_future_tsc(USB_TIME_DRST * 2); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) break; if (check_tsc(end)) { warn_timeout(); goto fail; } msleep(5); } // Reset complete. if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) // Device no longer present return -1; return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) >> USB_PORT_STAT_SPEED_SHIFT); fail: dprintf(1, "Failure on hub port %d reset\n", port); usb_hub_disconnect(hub, port); return -1; }
static void configure_ehci(void *data) { struct usb_ehci_s *cntl = data; // Allocate ram for schedule storage struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh)); struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); if (!fl || !intr_qh || !async_qh) { warn_noalloc(); goto fail; } // XXX - check for halted? // Reset the HC u32 cmd = readl(&cntl->regs->usbcmd); writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); u64 end = calc_future_tsc(250); for (;;) { cmd = readl(&cntl->regs->usbcmd); if (!(cmd & CMD_HCRESET)) break; if (check_tsc(end)) { warn_timeout(); goto fail; } yield(); } // Disable interrupts (just to be safe). writel(&cntl->regs->usbintr, 0); // Set schedule to point to primary intr queue head memset(intr_qh, 0, sizeof(*intr_qh)); intr_qh->next = EHCI_PTR_TERM; intr_qh->info2 = (0x01 << QH_SMASK_SHIFT); intr_qh->token = QTD_STS_HALT; intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM; int i; for (i=0; i<ARRAY_SIZE(fl->links); i++) fl->links[i] = (u32)intr_qh | EHCI_PTR_QH; writel(&cntl->regs->periodiclistbase, (u32)fl); // Set async list to point to primary async queue head memset(async_qh, 0, sizeof(*async_qh)); async_qh->next = (u32)async_qh | EHCI_PTR_QH; async_qh->info1 = QH_HEAD; async_qh->token = QTD_STS_HALT; async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM; cntl->async_qh = async_qh; writel(&cntl->regs->asynclistbase, (u32)async_qh); // Enable queues writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN); // Set default of high speed for root hub. writel(&cntl->regs->configflag, 1); cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK; // Find devices int count = check_ehci_ports(cntl); ehci_free_pipes(cntl); if (count) // Success return; // No devices found - shutdown and free controller. writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN); msleep(4); // 2ms to stop reading memory - XXX fail: free(fl); free(intr_qh); free(async_qh); free(cntl); }