123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /*
- * 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 <linux/irq.h>
- #include <linux/module.h>
- #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);
|