[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