[Gta04-owner] [PATCH] usb: various hacks related to otg
Andreas Kemnade
andreas at kemnade.info
Fri Nov 1 16:29:06 CET 2013
- start switch to b_host role via sysfs
- control vbus output power
- can be used to start charging when a charger with ID
grounded is first connected to the device and then to the
wall outlet
- enable/disable pulldown resistors via sysfs,
can be used to emulate a connection to a usb host,
useful for switching to b_host when using y-cables
- script for entering b_host mode
Signed-off-by: Andreas Kemnade <andreas at kemnade.info>
---
GTA04/README | 5 +++
GTA04/scripts/b_host.sh | 35 +++++++++++++++++
drivers/power/twl4030_charger.c | 12 ++++++
drivers/usb/musb/musb_core.c | 6 +++
drivers/usb/musb/omap2430.c | 15 ++++++-
drivers/usb/phy/phy-twl4030-usb.c | 78 +++++++++++++++++++++++++++++++++++++
6 files changed, 150 insertions(+), 1 deletion(-)
create mode 100755 GTA04/scripts/b_host.sh
diff --git a/GTA04/README b/GTA04/README
index dad8c0a..fcb638d 100644
--- a/GTA04/README
+++ b/GTA04/README
@@ -28,3 +28,8 @@
simple tasks
+ vibra.py shows how to program the vibrator as a rumble effect.
+ + b_host.sh helps to switch to b_host otg state (GTA04 is external
+ powered but plays the role of an usb host, the id pin is
+ not grounded in that situation). First apply power or switch on
+ software-controlled charging, run that script, then also connect
+ the usb data lines
diff --git a/GTA04/scripts/b_host.sh b/GTA04/scripts/b_host.sh
new file mode 100755
index 0000000..16e04c1
--- /dev/null
+++ b/GTA04/scripts/b_host.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# if not in software-controlled charging mode, Vbus (and only that)
+# has to be applied before running the script
+#MUSB_MODE=/sys/bus/platform/devices/musb-hdrc/mode
+#MUSB_MODE=/sys/bus/platform/devices/musb-hdrc.0/mode
+MUSB_MODE=/sys/bus/platform/devices/musb-hdrc.1.auto/mode
+
+MUSB_M=`cat $MUSB_MODE`
+echo "$MUSB_M"
+if [ "$MUSB_M" != b_idle ] ; then
+ echo Invalid mode at start
+ exit
+fi
+# trigger a transition from b_idle to b_peripheral by simulating
+# a connection to a host (enable pull-down resistors)
+echo on > /sys/bus/platform/devices/twl4030_usb/fake_host
+sleep 0.2
+cat $MUSB_MODE
+# trigger a transition from b_peripheral to b_hnp_enable
+echo host >$MUSB_MODE
+cat $MUSB_MODE
+sleep 0.2
+# simulate a disconnection from host
+echo off > /sys/bus/platform/devices/twl4030_usb/fake_host
+sleep 0.2
+cat $MUSB_MODE
+# be a host by ourselfs (if we wouldn't mess with this bit,
+# it would be already set here)
+echo on > /sys/bus/platform/devices/twl4030_usb/fake_host
+MUSB_M=`cat $MUSB_MODE`
+echo "$MUSB_M"
+
+if [ "$MUSB_M" = b_wait_acon ] ; then
+ echo "Prepared for being usb host, time to attach your device"
+fi
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index 70acb46..fd675c8 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -108,6 +108,9 @@ struct twl4030_bci {
unsigned long event;
};
+static struct twl4030_bci *bci_glob;
+
+
/*
* clear and set bits on an given register on a given module
*/
@@ -308,6 +311,13 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
return ret;
}
+/* ugly, pass bci_glob in another way */
+void twl4030_charger_enable_usb_ext(bool enable)
+{
+ if (bci_glob)
+ twl4030_charger_enable_usb(bci_glob, enable);
+}
+
/*
* Enable/Disable AC Charge funtionality.
*/
@@ -684,6 +694,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bci);
+ bci_glob = bci;
+
bci->ac.name = "twl4030_ac";
bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
bci->ac.properties = twl4030_charger_props;
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 41f8f49..6ac0fa9 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -658,9 +658,15 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
if (musb->is_active) {
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
+ /* If trying to switch to b_host without a real
+ * hnp reset (y-cables and such) the timer might
+ * be disturbing
+ */
+#if 0
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
OTG_TIME_B_ASE0_BRST));
+#endif
}
break;
case OTG_STATE_A_WAIT_BCON:
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 59d2245..0f968f0 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -208,8 +208,21 @@ static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode)
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
devctl |= MUSB_DEVCTL_SESSION;
- musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ /* this bit only works in the b_peripheral state
+ * to start the transition to b_wait_aconn
+ * and would normally be set through as a response
+ * to a b_hnp_enable
+ */
+
+ if (musb_mode == MUSB_HOST) {
+ devctl |= MUSB_DEVCTL_HR;
+ if (musb->g.is_otg)
+ musb->g.b_hnp_enable = 1;
+ } else if (musb_mode == MUSB_PERIPHERAL) {
+ devctl &= (~MUSB_DEVCTL_HR);
+ }
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
return 0;
}
diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c
index 88a1aed..fba508c 100644
--- a/drivers/usb/phy/phy-twl4030-usb.c
+++ b/drivers/usb/phy/phy-twl4030-usb.c
@@ -171,6 +171,7 @@ struct twl4030_usb {
/* internal define on top of container_of */
#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy)
+static int fake_host;
/*-------------------------------------------------------------------------*/
static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
@@ -544,6 +545,76 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
return 0;
}
+static ssize_t twl4030_usb_vbus_out_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct twl4030_usb *twl = dev_get_drvdata(dev);
+ /* hack, should probably not be called directly this way */
+ extern void twl4030_charger_enable_usb_ext(bool);
+
+ if (sysfs_streq(buf, "on")) {
+ twl4030_charger_enable_usb_ext(0);
+ twl4030_i2c_access(twl, 1);
+ twl4030_usb_set_bits(twl, ULPI_OTG_CTRL, ULPI_OTG_CTRL_DRVVBUS);
+ twl4030_i2c_access(twl, 0);
+ } else if (sysfs_streq(buf, "off")) {
+ twl4030_i2c_access(twl, 1);
+ twl4030_usb_clear_bits(twl, ULPI_OTG_CTRL,
+ ULPI_OTG_CTRL_DRVVBUS);
+ twl4030_i2c_access(twl, 0);
+ twl4030_charger_enable_usb_ext(1);
+ } else
+ return -EINVAL;
+ return n;
+}
+
+static ssize_t twl4030_usb_fake_host_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct twl4030_usb *twl = dev_get_drvdata(dev);
+ if (sysfs_streq(buf, "on"))
+ fake_host = 1;
+ else if (sysfs_streq(buf, "off"))
+ fake_host = 0;
+ else
+ return -EINVAL;
+ twl4030_i2c_access(twl, 1);
+ if (fake_host)
+ twl4030_usb_set_bits(twl, ULPI_OTG_CTRL,
+ ULPI_OTG_DP_PULLDOWN_DIS);
+ else
+ twl4030_usb_clear_bits(twl, ULPI_OTG_CTRL,
+ ULPI_OTG_DP_PULLDOWN_DIS);
+ twl4030_i2c_access(twl, 0);
+ return n;
+}
+
+static ssize_t twl4030_usb_fake_host_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ ret = sprintf(buf, "%s\n", fake_host ? "on" : "off");
+ return ret;
+}
+
+static ssize_t twl4030_usb_vbus_out_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct twl4030_usb *twl = dev_get_drvdata(dev);
+ int otg_ctrl = twl4030_usb_read(twl, ULPI_OTG_CTRL);
+ ret = sprintf(buf, "%s\n",
+ (otg_ctrl & ULPI_OTG_CTRL_DRVVBUS) ? "on" : "off");
+ return ret;
+}
+
+static DEVICE_ATTR(vbus_out, 0644, twl4030_usb_vbus_out_show,
+ twl4030_usb_vbus_out_store);
+
+static DEVICE_ATTR(fake_host, 0644, twl4030_usb_fake_host_show,
+ twl4030_usb_fake_host_store);
+
static ssize_t twl4030_usb_vbus_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -781,6 +852,11 @@ static int twl4030_usb_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "could not create sysfs file\n");
if (device_create_file(&pdev->dev, &dev_attr_id))
dev_warn(&pdev->dev, "could not create sysfs file\n");
+ if (device_create_file(&pdev->dev, &dev_attr_vbus_out))
+ dev_warn(&pdev->dev, "could not create sysfs file\n");
+ if (device_create_file(&pdev->dev, &dev_attr_fake_host))
+ dev_warn(&pdev->dev, "could not create sysfs file\n");
+
ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
@@ -814,6 +890,8 @@ static int twl4030_usb_remove(struct platform_device *pdev)
cancel_delayed_work(&twl->id_workaround_work);
device_remove_file(twl->dev, &dev_attr_id);
device_remove_file(twl->dev, &dev_attr_vbus);
+ device_remove_file(twl->dev, &dev_attr_vbus_out);
+ device_remove_file(twl->dev, &dev_attr_fake_host);
/* set transceiver mode to power on defaults */
twl4030_usb_set_mode(twl, -1);
--
1.7.10.4
More information about the Gta04-owner
mailing list