summaryrefslogtreecommitdiff
path: root/drivers/usb/otg
diff options
context:
space:
mode:
authorVenu Byravarasu <vbyravarasu@nvidia.com>2012-04-16 12:08:10 +0530
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-04-17 01:35:57 -0700
commitc1eb0f1dbbe7157c5031bafaf37868babba79c94 (patch)
treeaa06e3aa6060bf0dbb6601318ef61c1cbd403ad6 /drivers/usb/otg
parentafed1926614d18148d933888cf13d9490d8208be (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.c178
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: