/* * Work thread function for handling the USB power sequence. * * This work thread is created to avoid the pre-emption from the ISR context. * USB Power Rail and Vbus are controlled based on the USB cable connection. * USB Power rail function and VBUS control function cannot be called from ISR * as NvRmPmuSetVoltage() uses I2C driver, that waits on semaphore * during the I2C transaction this will cause the pre-emption if called in ISR. */ static void tegra_ehci_irq_work(struct work_struct* irq_work) { struct ehci_hcd *ehci = container_of(irq_work, struct ehci_hcd, irq_work); struct usb_hcd *hcd = ehci_to_hcd(ehci); struct tegra_hcd_platform_data *pdata; u32 status; bool kick_rhub = false; pdata = hcd->self.controller->platform_data; #ifdef CONFIG_USB_OTG_UTILS if (pdata->otg_mode && ehci->transceiver) { if (ehci->transceiver->state == OTG_STATE_A_HOST) { if (!ehci->host_reinited) { ehci->host_reinited = 1; tegra_ehci_power_up(hcd); if (hcd->state == HC_STATE_SUSPENDED) kick_rhub = true; tegra_ehci_restart(hcd); } } else if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) { if (ehci->host_reinited) { /* indicate hcd flags, that hardware is not accessible now */ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ehci_halt(ehci); tegra_ehci_power_down(hcd); ehci->transceiver->state = OTG_STATE_UNDEFINED; ehci->host_reinited = 0; } } } else #endif { if (pdata->id_detect == ID_PIN_CABLE_ID) { /* read otgsc register for ID pin status change */ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); /* Check pin status and enable/disable the power */ if (status & TEGRA_USB_ID_PIN_STATUS) { tegra_ehci_power_down(hcd); } else { tegra_ehci_power_up(hcd); if (hcd->state == HC_STATE_SUSPENDED) kick_rhub = true; } } } if (kick_rhub) { hcd->state = HC_STATE_SUSPENDED; usb_hcd_resume_root_hub(hcd); } }
static int tegra_ehci_bus_resume(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct tegra_hcd_platform_data *pdata; /* initialize the platform data pointer */ pdata = hcd->self.controller->platform_data; #ifdef CONFIG_USB_OTG_UTILS if (pdata->otg_mode && ehci->transceiver) { if (ehci->transceiver->state != OTG_STATE_A_HOST) { /* we are not in host mode, return */ return 0; } } #endif if (!pdata->otg_mode && pdata->id_detect==ID_PIN_CABLE_ID) { u32 status; /* read ID pin status */ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); /* If Id pin is high no host then return */ if (status & TEGRA_USB_ID_PIN_STATUS) { return 0; } } if (!ehci->host_resumed) { tegra_ehci_power_up(hcd); } return ehci_bus_resume(hcd); }
static void tegra_ehci_shutdown(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); /* ehci_shutdown touches the USB controller registers, make sure * controller has clocks to it */ if (!tegra->host_resumed) tegra_ehci_power_up(hcd); ehci_shutdown(hcd); }
static int tegra_ehci_resume(struct platform_device * pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct tegra_hcd_platform_data *pdata; u32 status; /* initialize the platform data pointer */ pdata = hcd->self.controller->platform_data; /* read otgsc register for ID pin status */ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); #ifdef CONFIG_USB_OTG_UTILS if (pdata->otg_mode && ehci->transceiver) { /* check if ID pin is high then no host return */ if (status & TEGRA_USB_ID_PIN_STATUS) { return 0; } else { /* set HCD flags to start host ISR */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ehci->host_reinited = 1; ehci->transceiver->state = OTG_STATE_A_HOST; } } #endif if (!pdata->otg_mode && pdata->id_detect==ID_PIN_CABLE_ID) { /* If no Id pin then return */ if (status & TEGRA_USB_ID_PIN_STATUS) { return 0; } } if (!ehci->host_resumed) { tegra_ehci_power_up(hcd); if (pdata->otg_mode) tegra_ehci_restart(hcd, HC_STATE_RUNNING); else if (!pdata->fast_wakeup) tegra_ehci_restart(hcd, hcd->state); } return 0; }
static int tegra_usb_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_regs __iomem *hw = ehci->regs; unsigned long val; set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); tegra_ehci_power_up(hcd); if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) { /* Wait for the phy to detect new devices * before we restart the controller */ msleep(10); goto restart; } /* Force the phy to keep data lines in suspend state */ tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); /* Enable host mode */ tdi_reset(ehci); /* Enable Port Power */ val = readl(&hw->port_status[0]); val |= PORT_POWER; writel(val, &hw->port_status[0]); udelay(10); /* Check if the phy resume from LP0. When the phy resume from LP0 * USB register will be reset. */ if (!readl(&hw->async_next)) { /* Program the field PTC based on the saved speed mode */ val = readl(&hw->port_status[0]); val &= ~PORT_TEST(~0); if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) val |= PORT_TEST_FORCE; else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) val |= PORT_TEST(6); else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) val |= PORT_TEST(7); writel(val, &hw->port_status[0]); udelay(10); /* Disable test mode by setting PTC field to NORMAL_OP */ val = readl(&hw->port_status[0]); val &= ~PORT_TEST(~0); writel(val, &hw->port_status[0]); udelay(10); } /* Poll until CCS is enabled */ if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, PORT_CONNECT, 2000)) { pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); goto restart; } /* Poll until PE is enabled */ if (handshake(ehci, &hw->port_status[0], PORT_PE, PORT_PE, 2000)) { pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); goto restart; } /* Clear the PCI status, to avoid an interrupt taken upon resume */ val = readl(&hw->status); val |= STS_PCD; writel(val, &hw->status); /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ val = readl(&hw->port_status[0]); if ((val & PORT_POWER) && (val & PORT_PE)) { val |= PORT_SUSPEND; writel(val, &hw->port_status[0]); /* Need a 4ms delay before the controller goes to suspend */ mdelay(4); /* Wait until port suspend completes */ if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, PORT_SUSPEND, 1000)) { pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); goto restart; } } tegra_ehci_phy_restore_end(tegra->phy); return 0; restart: if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) tegra_ehci_phy_restore_end(tegra->phy); tegra_ehci_restart(hcd); return 0; }
static int tegra_ehci_probe(struct platform_device *pdev) { struct resource *res; struct tegra_hcd_platform_data *pdata = pdev->dev.platform_data; struct usb_hcd *hcd; int e = 0; int irq; unsigned int temp; struct ehci_hcd *ehci; if (!pdata) { dev_err(&pdev->dev, "platform data must be specified\n"); return -EINVAL; } WARN_ON(!pdev->dev.coherent_dma_mask || !pdev->dev.dma_mask); hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (IS_ERR_OR_NULL(hcd)) { dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; } if (pdata->id_detect == ID_PIN_GPIO) { e = gpio_request(pdata->gpio_nr, dev_name(&pdev->dev)); if (e < 0) { dev_err(&pdev->dev, "request ID pin GPIO failed\n"); goto fail_hcd; } e = gpio_direction_input(pdata->gpio_nr); if (e < 0) { dev_err(&pdev->dev, "failed to set ID pin as input\n"); goto fail_gpio; } } INIT_WORK(&pdata->work, tegra_ehci_busy_hint_work); /* Init the tegra USB phy */ if (NvDdkUsbPhyOpen(s_hRmGlobal, pdata->instance, &pdata->hUsbPhy) != NvSuccess) { dev_err(&pdev->dev, "failed to open USB phy DDK\n"); e = -ENODEV; goto fail_gpio; } tegra_ehci_power_up(hcd); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get I/O memory\n"); e = -ENXIO; goto fail_phy; } if (!pdata->otg_mode) { res = request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev)); if (!res) { dev_err(&pdev->dev, "resource in use\n"); e = -EBUSY; goto fail_phy; } } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); hcd->regs = ioremap(res->start, resource_size(res)); if (!hcd->regs) { e = -ENOMEM; goto fail_mem; } /* Set to Host mode by setting bit 0-1 of USB device mode register */ temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); writel((temp | TEGRA_USB_USBMODE_HOST), (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); irq = platform_get_irq(pdev, 0); if (!irq) { e = -ENODEV; goto fail_iomap; } set_irq_flags(irq, IRQF_VALID); ehci = hcd_to_ehci(hcd); INIT_WORK(&ehci->irq_work, tegra_ehci_irq_work); e = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (e != 0) { dev_err(&pdev->dev, "failed to add HCD\n"); goto fail_iomap; } platform_set_drvdata(pdev, hcd); #ifdef CONFIG_DMABOUNCE e = dmabounce_register_dev(&pdev->dev, 1024, 32768); if (e != 0) { dev_err(&pdev->dev, "failed to register DMA bounce\n"); goto fail_add; } #endif #ifdef CONFIG_USB_OTG_UTILS if (pdata->otg_mode) { ehci->transceiver = otg_get_transceiver(); if (!ehci->transceiver) { dev_err(&pdev->dev, "Failed to get OTG transceiver\n"); e = -ENODEV; goto fail_dmabounce; } otg_set_host(ehci->transceiver, (struct usb_bus *)hcd); /* Stop the controller and power down the phy, OTG will * start the host driver based on the ID pin * detection */ ehci_halt(ehci); /* reset the host and put the controller in idle mode */ temp = ehci_readl(ehci, &ehci->regs->command); temp |= CMD_RESET; ehci_writel(ehci, temp, &ehci->regs->command); temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); writel((temp & ~TEGRA_USB_USBMODE_HOST), (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); /* indicate hcd flags, that hardware is not accessable now * in host mode*/ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); tegra_ehci_power_down(hcd); ehci->host_reinited = 0; } else #endif { if (pdata->id_detect == ID_PIN_CABLE_ID) { /* enable the cable ID interrupt */ temp = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); temp |= TEGRA_USB_ID_INT_ENABLE; temp |= TEGRA_USB_ID_PIN_WAKEUP_ENABLE; writel(temp, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET)); /* Check if we detect any device connected */ if (temp & TEGRA_USB_ID_PIN_STATUS) { tegra_ehci_power_down(hcd); } else { tegra_ehci_power_up(hcd); } } } return 0; fail_dmabounce: #ifdef CONFIG_DMABOUNCE dmabounce_unregister_dev(&pdev->dev); #endif fail_add: usb_remove_hcd(hcd); fail_iomap: iounmap(hcd->regs); fail_mem: res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); fail_phy: NvDdkUsbPhyClose(pdata->hUsbPhy); fail_gpio: if (pdata->gpio_nr) gpio_free(pdata->gpio_nr); fail_hcd: usb_put_hcd(hcd); return e; }
static int tegra_usb_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); struct tegra_ehci_context *context = &tegra->context; struct ehci_regs __iomem *hw = tegra->ehci->regs; unsigned long val; int lp0_resume = 0; set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); tegra_ehci_power_up(ehci_to_hcd(tegra->ehci)); if (!context->valid) { /* Wait for the phy to detect new devices * before we restart the controller */ msleep(10); goto restart; } tegra_ehci_phy_restore_start(tegra->phy, context->port_speed); /* Check if the phy resume from LP0. When the phy resume from LP0 * USB register will be reset. */ if (!readl(&hw->async_next)) lp0_resume = 1; /* Restore register context */ writel(TEGRA_USB_USBMODE_HOST, &hw->reserved[19]); writel(context->otgsc, &hw->reserved[18]); writel(context->txfilltunning, &hw->reserved[2]); writel(context->async_next, &hw->async_next); writel(context->frame_list, &hw->frame_list); writel(context->command, &hw->command); /* Enable Port Power */ val = readl(&hw->port_status[0]); val |= PORT_POWER; writel(val, &hw->port_status[0]); udelay(10); if (lp0_resume) { /* Program the field PTC in PORTSC based on the saved speed mode */ val = readl(&hw->port_status[0]); val &= ~(TEGRA_USB_PORTSC1_PTC(~0)); if (context->port_speed == TEGRA_USB_PHY_PORT_HIGH) val |= TEGRA_USB_PORTSC1_PTC(5); else if (context->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) val |= TEGRA_USB_PORTSC1_PTC(6); else if (context->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) val |= TEGRA_USB_PORTSC1_PTC(7); writel(val, &hw->port_status[0]); udelay(10); } /* Disable test mode by setting PTC field to NORMAL_OP */ val = readl(&hw->port_status[0]); val &= ~(TEGRA_USB_PORTSC1_PTC(~0)); writel(val, &hw->port_status[0]); udelay(10); /* Poll until CCS is enabled */ if (handshake(tegra->ehci, &hw->port_status[0], PORT_CONNECT, PORT_CONNECT, 2000)) { pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); goto restart; } /* Poll until PE is enabled */ if (handshake(tegra->ehci, &hw->port_status[0], PORT_PE, PORT_PE, 2000)) { pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); goto restart; } /* Clear the PCI status, to avoid an interrupt taken upon resume */ val = readl(&hw->status); val |= STS_PCD; writel(val, &hw->status); /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ val = readl(&hw->port_status[0]); if ((val & PORT_POWER) && (val & PORT_PE)) { val |= PORT_SUSPEND; writel(val, &hw->port_status[0]); /* Wait until port suspend completes */ if (handshake(tegra->ehci, &hw->port_status[0], PORT_SUSPEND, PORT_SUSPEND, 1000)) { pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); goto restart; } } tegra_ehci_phy_restore_end(tegra->phy); return 0; restart: if (context->valid) tegra_ehci_phy_restore_end(tegra->phy); tegra_ehci_restart(hcd); return 0; }