diff options
author | Venu Byravarasu <vbyravarasu@nvidia.com> | 2012-04-16 12:08:10 +0530 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-04-17 01:35:57 -0700 |
commit | c1eb0f1dbbe7157c5031bafaf37868babba79c94 (patch) | |
tree | aa06e3aa6060bf0dbb6601318ef61c1cbd403ad6 /drivers/usb/otg | |
parent | afed1926614d18148d933888cf13d9490d8208be (diff) |
usb: otg: tegra: set & read otg state with sysfs
Add support for setting & reading back OTG state with sysfs
bug 947300
Change-Id: I178c3eb6e2b227ca11fee8916e38c6677d3e4cb0
Signed-off-by: Venu Byravarasu <vbyravarasu@nvidia.com>
Reviewed-on: http://git-master/r/96660
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r-- | drivers/usb/otg/tegra-otg.c | 178 |
1 files changed, 129 insertions, 49 deletions
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 4c04e6e183f1..7d93d751df41 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -46,6 +46,12 @@ typedef void (*callback_t)(enum usb_otg_state to, enum usb_otg_state from, void *args); +#ifdef DEBUG +#define DBG(stuff...) pr_info("tegra-otg: " stuff) +#else +#define DBG(stuff...) do {} while (0) +#endif + struct tegra_otg_data { struct otg_transceiver otg; unsigned long int_status; @@ -61,6 +67,7 @@ struct tegra_otg_data { callback_t charger_cb; void *charger_cb_data; + bool interrupt_mode; }; static struct tegra_otg_data *tegra_clone; @@ -106,6 +113,27 @@ static const char *tegra_state_name(enum usb_otg_state state) } } +static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en) +{ + unsigned long val; + + clk_enable(tegra->clk); + val = otg_readl(tegra, USB_PHY_WAKEUP); + if (en) { + val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); + val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); + } else { + val &= ~(USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); + val &= ~(USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); + } + otg_writel(tegra, val, USB_PHY_WAKEUP); + /* Add delay to make sure register is updated */ + udelay(1); + clk_disable(tegra->clk); + + return val; +} + static struct platform_device * tegra_usb_otg_host_register(struct platform_device *ehci_device, struct tegra_ehci_platform_data *pdata) @@ -182,6 +210,40 @@ int register_otg_callback(callback_t cb, void *args) } EXPORT_SYMBOL_GPL(register_otg_callback); +static void tegra_change_otg_state(struct tegra_otg_data *tegra, + enum usb_otg_state to) +{ + struct otg_transceiver *otg = &tegra->otg; + enum usb_otg_state from = otg->state; + + if(!tegra->interrupt_mode){ + DBG("OTG: Vbus detection is disabled"); + return; + } + + DBG("%s(%d) requested otg state %s-->%s\n", __func__, + __LINE__, tegra_state_name(from), tegra_state_name(to)); + + if (to != OTG_STATE_UNDEFINED && from != to) { + otg->state = to; + dev_info(tegra->otg.dev, "%s --> %s\n", tegra_state_name(from), + tegra_state_name(to)); + + if (from == OTG_STATE_A_SUSPEND) { + if (to == OTG_STATE_B_PERIPHERAL && otg->gadget) + usb_gadget_vbus_connect(otg->gadget); + else if (to == OTG_STATE_A_HOST) + tegra_start_host(tegra); + } else if (from == OTG_STATE_A_HOST) { + if (to == OTG_STATE_A_SUSPEND) + tegra_stop_host(tegra); + } else if (from == OTG_STATE_B_PERIPHERAL && otg->gadget) { + if (to == OTG_STATE_A_SUSPEND) + usb_gadget_vbus_disconnect(otg->gadget); + } + } +} + static void irq_work(struct work_struct *work) { struct tegra_otg_data *tegra = @@ -204,50 +266,27 @@ static void irq_work(struct work_struct *work) status = tegra->int_status; - if (tegra->int_status & USB_ID_INT_STATUS) { - if (status & USB_ID_STATUS) { - if ((status & USB_VBUS_STATUS) && (from != OTG_STATE_A_HOST)) - to = OTG_STATE_B_PERIPHERAL; - else - to = OTG_STATE_A_SUSPEND; - } - else - to = OTG_STATE_A_HOST; - } - if (from != OTG_STATE_A_HOST) { - if (tegra->int_status & USB_VBUS_INT_STATUS) { - if (status & USB_VBUS_STATUS) - to = OTG_STATE_B_PERIPHERAL; - else - to = OTG_STATE_A_SUSPEND; - } - } - spin_unlock_irqrestore(&tegra->lock, flags); - - if (to != OTG_STATE_UNDEFINED) { - otg->state = to; - - dev_info(tegra->otg.dev, "%s --> %s\n", tegra_state_name(from), - tegra_state_name(to)); - - if (tegra->charger_cb) - tegra->charger_cb(to, from, tegra->charger_cb_data); - - if (to == OTG_STATE_A_SUSPEND) { - if (from == OTG_STATE_A_HOST) - tegra_stop_host(tegra); - else if (from == OTG_STATE_B_PERIPHERAL && otg->gadget) - usb_gadget_vbus_disconnect(otg->gadget); - } else if (to == OTG_STATE_B_PERIPHERAL && otg->gadget) { - if (from == OTG_STATE_A_SUSPEND) - usb_gadget_vbus_connect(otg->gadget); - } else if (to == OTG_STATE_A_HOST) { - if (from == OTG_STATE_A_SUSPEND) - tegra_start_host(tegra); - } + /* Debug prints */ + DBG("%s(%d) status = 0x%x\n", __func__, __LINE__, status); + if ((status & USB_ID_INT_STATUS) && + (status & USB_VBUS_INT_STATUS)) + DBG("%s(%d) got vbus & id interrupt\n", __func__, __LINE__); + else { + if (status & USB_ID_INT_STATUS) + DBG("%s(%d) got id interrupt\n", __func__, __LINE__); + if (status & USB_VBUS_INT_STATUS) + DBG("%s(%d) got vbus interrupt\n", __func__, __LINE__); } + if (!(status & USB_ID_STATUS)) + to = OTG_STATE_A_HOST; + else if (status & USB_VBUS_STATUS && from != OTG_STATE_A_HOST) + to = OTG_STATE_B_PERIPHERAL; + else + to = OTG_STATE_A_SUSPEND; + spin_unlock_irqrestore(&tegra->lock, flags); + tegra_change_otg_state(tegra, to); clk_disable(tegra->clk); tegra_otg_disable_clk(); } @@ -291,14 +330,7 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg, tegra = container_of(otg, struct tegra_otg_data, otg); otg->gadget = gadget; - clk_enable(tegra->clk); - val = otg_readl(tegra, USB_PHY_WAKEUP); - val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); - val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); - otg_writel(tegra, val, USB_PHY_WAKEUP); - /* Add delay to make sure register is updated */ - udelay(1); - clk_disable(tegra->clk); + val = enable_interrupt(tegra, true); if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) { val |= USB_VBUS_INT_STATUS; @@ -347,6 +379,46 @@ static int tegra_otg_set_suspend(struct otg_transceiver *otg, int suspend) return 0; } +static ssize_t show_host_en(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_otg_data *tegra = platform_get_drvdata(pdev); + + *buf = tegra->interrupt_mode ? '0': '1'; + strcat(buf, "\n"); + return strlen(buf); +} + +static ssize_t store_host_en(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_otg_data *tegra = platform_get_drvdata(pdev); + unsigned long host; + int err; + + err = kstrtoul(buf, 10, &host); + if (err < 0) { + return err; + } + + if (host) { + enable_interrupt(tegra, false); + tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND); + tegra_change_otg_state(tegra, OTG_STATE_A_HOST); + tegra->interrupt_mode = false; + } else { + tegra->interrupt_mode = true; + tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND); + enable_interrupt(tegra, true); + } + + return count; +} + +static DEVICE_ATTR(enable_host, 0644, show_host_en, store_host_en); + static int tegra_otg_probe(struct platform_device *pdev) { struct tegra_otg_data *tegra; @@ -373,6 +445,7 @@ static int tegra_otg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tegra); tegra_clone = tegra; tegra->clk_enabled = false; + tegra->interrupt_mode = true; tegra->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(tegra->clk)) { @@ -424,6 +497,13 @@ static int tegra_otg_probe(struct platform_device *pdev) if (!ehci_pdata->default_enable) clk_disable(tegra->clk); dev_info(&pdev->dev, "otg transceiver registered\n"); + + err = device_create_file(&pdev->dev, &dev_attr_enable_host); + if (err) { + dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); + goto err_irq; + } + return 0; err_irq: |