/* * xHCI host controller driver * * Copyright (C) 2008 Intel Corp. * * Author: Sarah Sharp * Some code borrowed from the Linux EHCI driver. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "xhci.h" #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" /* TODO: copied from ehci-hcd.c - can this be refactored? */ /* * handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read * @mask: bits to look at in result of read * @done: value of those bits when handshake succeeds * @usec: timeout in microseconds * * Returns negative errno, or zero on success * * Success happens when the "mask" bits have the specified value (hardware * handshake done). There are two failure modes: "usec" have passed (major * hardware flakeout), or the register reads as all-ones (hardware removed). */ static int handshake(struct xhci_hcd *xhci, void __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; do { result = xhci_readl(xhci, ptr); if (result == ~(u32)0) /* card removed */ return -ENODEV; result &= mask; if (result == done) return 0; udelay(1); usec--; } while (usec > 0); return -ETIMEDOUT; } /* * Force HC into halt state. * * Disable any IRQs and clear the run/stop bit. * HC will complete any current and actively pipelined transactions, and * should halt within 16 microframes of the run/stop bit being cleared. * Read HC Halted bit in the status register to see when the HC is finished. * XXX: shouldn't we set HC_STATE_HALT here somewhere? */ int xhci_halt(struct xhci_hcd *xhci) { u32 halted; u32 cmd; u32 mask; xhci_dbg(xhci, "// Halt the HC\n"); /* Disable all interrupts from the host controller */ mask = ~(XHCI_IRQS); halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT; if (!halted) mask &= ~CMD_RUN; cmd = xhci_readl(xhci, &xhci->op_regs->command); cmd &= mask; xhci_writel(xhci, cmd, &xhci->op_regs->command); return handshake(xhci, &xhci->op_regs->status, STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); } /* * Reset a halted HC, and set the internal HC state to HC_STATE_HALT. * * This resets pipelines, timers, counters, state machines, etc. * Transactions will be terminated immediately, and operational registers * will be set to their defaults. */ int xhci_reset(struct xhci_hcd *xhci) { u32 command; u32 state; state = xhci_readl(xhci, &xhci->op_regs->status); BUG_ON((state & STS_HALT) == 0); xhci_dbg(xhci, "// Reset the HC\n"); command = xhci_readl(xhci, &xhci->op_regs->command); command |= CMD_RESET; xhci_writel(xhci, command, &xhci->op_regs->command); /* XXX: Why does EHCI set this here? Shouldn't other code do this? */ xhci_to_hcd(xhci)->state = HC_STATE_HALT; return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000); } /* * Stop the HC from processing the endpoint queues. */ static void xhci_quiesce(struct xhci_hcd *xhci) { /* * Queues are per endpoint, so we need to disable an endpoint or slot. * * To disable a slot, we need to insert a disable slot command on the * command ring and ring the doorbell. This will also free any internal * resources associated with the slot (which might not be what we want). * * A Release Endpoint command sounds better - doesn't free internal HC * memory, but removes the endpoints from the schedule and releases the * bandwidth, disables the doorbells, and clears the endpoint enable * flag. Usually used prior to a set interface command. * * TODO: Implement after command ring code is done. */ BUG_ON(!HC_IS_RUNNING(xhci_to_hcd(xhci)->state)); xhci_dbg(xhci, "Finished quiescing -- code not written yet\n"); } #if 0 /* Set up MSI-X table for entry 0 (may claim other entries later) */ static int xhci_setup_msix(struct xhci_hcd *xhci) { int ret; struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); xhci->msix_count = 0; /* XXX: did I do this right? ixgbe does kcalloc for more than one */ xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL); if (!xhci->msix_entries) { xhci_err(xhci, "Failed to allocate MSI-X entries\n"); return -ENOMEM; } xhci->msix_entries[0].entry = 0; ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); if (ret) { xhci_err(xhci, "Failed to enable MSI-X\n"); goto free_entries; } /* * Pass the xhci pointer value as the request_irq "cookie". * If more irqs are added, this will need to be unique for each one. */ ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0, "xHCI", xhci_to_hcd(xhci)); if (ret) { xhci_err(xhci, "Failed to allocate MSI-X interrupt\n"); goto disable_msix; } xhci_dbg(xhci, "Finished setting up MSI-X\n"); return 0; disable_msix: pci_disable_msix(pdev); free_entries: kfree(xhci->msix_entries); xhci->msix_entries = NULL; return ret; } /* XXX: code duplication; can xhci_setup_msix call this? */ /* Free any IRQs and disable MSI-X */ static void xhci_cleanup_msix(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (!xhci->msix_entries) return; free_irq(xhci->msix_entries[0].vector, xhci); pci_disable_msix(pdev); kfree(xhci->msix_entries); xhci->msix_entries = NULL; xhci_dbg(xhci, "Finished cleaning up MSI-X\n"); } #endif /* * Initialize memory for HCD and xHC (one-time init). * * Program the PAGESIZE register, initialize the device context array, create * device contexts (?), set up a command ring segment (or two?), create event * ring (one for now). */ int xhci_init(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int retval = 0; xhci_dbg(xhci, "xhci_init\n"); spin_lock_init(&xhci->lock); retval = xhci_mem_init(xhci, GFP_KERNEL); xhci_dbg(xhci, "Finished xhci_init\n"); return retval; } /* * Start the HC after it was halted. * * This function is called by the USB core when the HC driver is added. * Its opposite is xhci_stop(). * * xhci_init() must be called once before this function can be called. * Reset the HC, enable device slot contexts, program DCBAAP, and * set command ring pointer and event ring pointer. * * Setup MSI-X vectors and enable interrupts. */ int xhci_run(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); xhci_dbg(xhci, "xhci_run\n"); #if 0 /* FIXME: MSI not setup yet */ /* Do this at the very last minute */ ret = xhci_setup_msix(xhci); if (!ret) return ret; return -ENOSYS; #endif xhci_dbg(xhci, "// Set the interrupt modulation register\n"); temp = xhci_readl(xhci, &xhci->ir_set->irq_control); temp &= 0xffff; temp |= (u32) 160; xhci_writel(xhci, temp, &xhci->ir_set->irq_control); /* Set the HCD state before we enable the irqs */ hcd->state = HC_STATE_RUNNING; temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= (CMD_EIE); xhci_dbg(xhci, "// Enable interrupts, cmd = 0x%x.\n", temp); xhci_writel(xhci, temp, &xhci->op_regs->command); temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); xhci_dbg(xhci, "// Enabling event ring interrupter 0x%x" " by writing 0x%x to irq_pending\n", (unsigned int) xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); xhci_writel(xhci, ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, xhci->ir_set, 0); temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= (CMD_RUN); xhci_dbg(xhci, "// Turn on HC, cmd = 0x%x.\n", temp); xhci_writel(xhci, temp, &xhci->op_regs->command); /* Flush PCI posted writes */ temp = xhci_readl(xhci, &xhci->op_regs->command); xhci_dbg(xhci, "// @%x = 0x%x\n", (unsigned int) &xhci->op_regs->command, temp); xhci_dbg(xhci, "Finished xhci_run\n"); return 0; } /* * Stop xHCI driver. * * This function is called by the USB core when the HC driver is removed. * Its opposite is xhci_run(). * * Disable device contexts, disable IRQs, and quiesce the HC. * Reset the HC, finish any completed transactions, and cleanup memory. */ void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); spin_lock_irq(&xhci->lock); if (HC_IS_RUNNING(hcd->state)) xhci_quiesce(xhci); xhci_halt(xhci); xhci_reset(xhci); spin_unlock_irq(&xhci->lock); #if 0 /* No MSI yet */ xhci_cleanup_msix(xhci); #endif xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); xhci_writel(xhci, ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, xhci->ir_set, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); xhci_dbg(xhci, "xhci_stop completed - status = %x\n", xhci_readl(xhci, &xhci->op_regs->status)); } /* * Shutdown HC (not bus-specific) * * This is called when the machine is rebooting or halting. We assume that the * machine will be powered off, and the HC's internal state will be reset. * Don't bother to free memory. */ void xhci_shutdown(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); spin_lock_irq(&xhci->lock); xhci_halt(xhci); spin_unlock_irq(&xhci->lock); #if 0 xhci_cleanup_msix(xhci); #endif xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", xhci_readl(xhci, &xhci->op_regs->status)); } int xhci_get_frame(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); /* EHCI mods by the periodic size. Why? */ return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3; } MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { #ifdef CONFIG_PCI int retval = 0; retval = xhci_register_pci(); if (retval < 0) { printk(KERN_DEBUG "Problem registering PCI driver."); return retval; } #endif return 0; } module_init(xhci_hcd_init); static void __exit xhci_hcd_cleanup(void) { #ifdef CONFIG_PCI xhci_unregister_pci(); #endif } module_exit(xhci_hcd_cleanup);