예제 #1
0
/*
 * 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);
	}
}
예제 #2
0
static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct usb_hcd *hcd = platform_get_drvdata(pdev);
	struct ehci_hcd *ehci = hcd_to_ehci(hcd);

#ifdef CONFIG_USB_OTG_UTILS
	struct tegra_hcd_platform_data *pdata;
	/* initialize the platform data pointer */
	pdata = hcd->self.controller->platform_data;
	if (pdata->otg_mode && ehci->transceiver) {
		if (ehci->transceiver->state != OTG_STATE_A_HOST) {
			/* we are not in host mode, return */
			return 0;
		}
		else {
			ehci->transceiver->state = OTG_STATE_UNDEFINED;
			ehci->host_reinited = 0;
			/* indicate hcd flags, that hardware is not accessable now */
			clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
		}
	}
#endif

	if (ehci->host_resumed) {
		/* halt the controller before powering down the phy */
		ehci_halt(ehci);
		tegra_ehci_power_down(hcd);
	}

	return 0;
}
static int tegra_usb_suspend(struct usb_hcd *hcd)
{
	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
	struct ehci_regs __iomem *hw = tegra->ehci->regs;
	struct tegra_ehci_context *context = &tegra->context;
	unsigned long flags;

	spin_lock_irqsave(&tegra->ehci->lock, flags);

	context->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;

	if (context->port_speed > TEGRA_USB_PHY_PORT_HIGH) {
		/* If no device connection or invalid speeds,
		 * don't save the context */
		context->valid = false;
	} else {
		context->command	= readl(&hw->command);
		context->frame_list	= readl(&hw->frame_list);
		context->async_next	= readl(&hw->async_next);
		context->txfilltunning	= readl(&hw->reserved[2]);
		context->otgsc		= readl(&hw->reserved[18]);
		context->valid = true;
	}

	ehci_halt(tegra->ehci);
	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);

	spin_unlock_irqrestore(&tegra->ehci->lock, flags);

	tegra_ehci_power_down(ehci_to_hcd(tegra->ehci));
	return 0;
}
예제 #4
0
static void tegra_ehci_shutdown (struct usb_hcd *hcd)
{
	struct ehci_hcd *ehci = hcd_to_ehci(hcd);

	if (ehci->host_resumed) {
		/* call ehci shut down */
		ehci_shutdown(hcd);
		/* we are ready to shut down, powerdown the phy */
		tegra_ehci_power_down(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);

	/* call ehci shut down */
	ehci_shutdown(hcd);

	/* we are ready to shut down, powerdown the phy */
	tegra_ehci_power_down(hcd);
}
예제 #6
0
static int tegra_usb_suspend(struct usb_hcd *hcd)
{
	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
	struct ehci_regs __iomem *hw = tegra->ehci->regs;
	unsigned long flags;

	spin_lock_irqsave(&tegra->ehci->lock, flags);

	tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;

	tegra_ehci_halt(tegra->ehci);

	spin_unlock_irqrestore(&tegra->ehci->lock, flags);

	tegra_ehci_power_down(hcd);
	return 0;
}
예제 #7
0
void tegra_ehci_enable_host (struct usb_hcd *hcd)
{
	struct ehci_hcd *ehci = hcd_to_ehci (hcd);
	unsigned long flags;

	spin_lock_irqsave (&ehci->lock, flags);

	if (ehci->transceiver->state == OTG_STATE_A_HOST) {
		if (!ehci->host_reinited) {
			schedule_work(&ehci->irq_work);
			spin_unlock_irqrestore (&ehci->lock, flags);
			return;
		} else
			printk(KERN_DEBUG "%s host initialized already\n", __func__);
	} else if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) {
		if (!ehci->host_reinited) {
			printk(KERN_DEBUG "%s host not initialized\n", __func__);
			spin_unlock_irqrestore (&ehci->lock, flags);
			return;
		}
		else {
			/* Force the disconnect because otherwise we could get
			 * left in the connected state if the last getPorStatus
			 * request comes in before the HOST -> SUSPEND state
			 * change. */
			spin_unlock_irqrestore (&ehci->lock, flags);
			/* 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;
			return;
		}
	} else
		printk(KERN_DEBUG "%s unexpected state\n", __func__);

	spin_unlock_irqrestore (&ehci->lock, flags);
	ehci_irq(hcd);

	return;
}
예제 #8
0
static int tegra_ehci_bus_suspend(struct usb_hcd *hcd)
{
	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
	struct tegra_hcd_platform_data *pdata;
	int error_status = 0;

	/* 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;
		} else {
			ehci->transceiver->state = OTG_STATE_A_SUSPEND;
			ehci->host_reinited = 0;
		}
	}
#endif

	if (!pdata->otg_mode && pdata->id_detect==ID_PIN_CABLE_ID) {
		u32 status;
		/* check for ID pin status */
		status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
		/* If Id pin is high means host is not connected return */
		if (status & TEGRA_USB_ID_PIN_STATUS) {
			return 0;
		}
	}

	if (ehci->host_resumed) {
		error_status = ehci_bus_suspend(hcd);
		if (!error_status)
			tegra_ehci_power_down(hcd);
	}

	return error_status;
}
예제 #9
0
static int tegra_ehci_hub_control (
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
) {
	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
	u32 __iomem	*status_reg = &ehci->regs->port_status[
				(wIndex & 0xff) - 1];
	u32		temp;
	struct tegra_hcd_platform_data *pdata;
	unsigned long	flags;
	int		retval = 0;

	/* initialize the platform data pointer */
	pdata = hcd->self.controller->platform_data;

	/* if hardware is not accessable then don't read the registers */
	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || !ehci->host_resumed) {
		if (buf)
			memset (buf, 0, wLength);
		return retval;
	}

	/* In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits
	 * that are write on clear, by wrting back the register read value, so
	 * USB_PORT_FEAT_ENABLE is handled here by masking the set on clear bits */
	if ((typeReq == ClearPortFeature) && (wValue == USB_PORT_FEAT_ENABLE)) {
		spin_lock_irqsave (&ehci->lock, flags);
		temp = ehci_readl(ehci, status_reg);
		ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg);
		spin_unlock_irqrestore (&ehci->lock, flags);
		return retval;
	}

	/* Handle the hub control events here */
	retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);

	/* Power down the USB phy when there is no port connection and all
	 * HUB events are cleared by checking the lower four bits
	 * (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC) */
#ifdef CONFIG_USB_OTG_UTILS
	if (pdata->otg_mode && ehci->transceiver) {
		if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) {
			temp = ehci_readl(ehci, status_reg);
			if (!(temp & (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC))
				&& ehci->host_reinited) {
				/* indicate hcd flags, that hardware is not accessable 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;
			}
		}
	}
#endif

	return retval;
}
예제 #10
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;
}