So reading the Battery Charging Implementers spec, and in particular, this doc, Section 6 is the gory details. (and table 5.3).

Table 5.3 suggests that RID_A  should be 124K (oops, I used 100K), RID_B = 68K, and RID_C = 36.5K.

And, it says that RID_A is 122 < RID_A < 126K, so there’s no way my 100K is in that range. Its < 5% tolerance too.

Hmmm, maybe its more time w/ the soldering iron that is warranted.


… later …

Now w/ the correct 124K. The interesting bit is just need CONFIG_USB_MSM_ACA=y, and not CONFIG_USB_MSM_STANDARD_ACA. So yes it charged w/ the OTG cable.

And very briefly it does both OTG USB and charge.

But no dice on it working correctly all the time.

Time to dig in to the differences in the various code branches. On AOSP for MSM,, there is a 'newer' branch. Newer in the sense that it is a newer kernel.

To diff the version shipped w/ the Nexus 7 vs the newer one, run git diff remotes/origin/android-msm-3.9-usb-and-mmc-hacks — msm_otg.c

(or just from the top if you have time!)

One interesting area is msm_hsusb_ldo_enable(). The ‘newer’ version has more cases than just off/on.

It also looks like msm_otg_phy_reset() needs a short delay.

msm_otg_suspend() also has a small piece that could be merged

msm_otg_notify_power_supply() is also interesting. In my version, its >0 mA, in the 3.9… version, its >2mA.

msm_otg_set_host() is interesting.  There’s a chunk that the FLO branch has a) commented out, and b) ‘if mako’

The USB_CHG_STATE_DETECTED case in msm_chg_detect_work() is intriguing.

OTG_STATE_B_PERIPHERAL case in msm_otg_sm_work() interesting, there is another case here (for the DCP, dedicated charger, e.g. just V+/GND).

The  “} else if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {” part of msm_otg_irq()  also looks like it needs merging.

Merging the power_prop part could also be useful. And maybe the ‘pnoc errata fix’ (hmm, can’t find any info online about what that HW errata might be).

The bottom of msm_otg_probe() has some pm8921_charger_register… bits in the newer one.



So i can reliably charge now with the USB OTG cable.

I found I had a problem wit the cable I had bought: it should have 100K between pin 4 & pin 5, but pin 4 was No-Connect. E.g. see here.

I cut the shield off the cable i was using with an xacto knife (this is pretty easy, there’s a seam).


From here I could see it not only didn’t have the resistor on pin 4-5, but it didn’t even bring pin-4 out to the little ‘circuit board’ for soldering to. Crap.


Fortunately USB cables are the cockroaches of our generation, and i had a few to sacrifice to the cause just laying around.  So i slit the next one open, spliced two cables (red-red, black-black, 100K from pin 4-5, and white/green one-off to one connector). [Note: later i learned this needs to be 124K. the online site i found was wrong, i looked it up in the USB Battery Charging standard]


Now the system detects that its a OTG charging cable, and starts/stops the charging when I plug in power.

With my patch in msm_otg.c, I call the smb345 charger instead of the pm8921 charger. But i run into some other troubles. The smb345 driver does not implement the ‘power properties’ (e.g. see the smb349 driver which does, e.g. see POWER_SUPPLY_PROP_CURRENT_MAX). So msm_otg_notify_power_supply() fails (since power_supply_set_current goes nowhere).

I’m also not sure what to stub in for pm8921_charger_vbus_draw.

So without this, it seems to leave the actual device in b_idle state, which is pointless. I can charge but no accessories!

OK, enabled CONFIG_USB_MSM_ACA=y, and made the diffs as per previous post, and echo enabled > /sys/kernel/debug/msm_otg/aca

now the device recognises (and starts charging) when I plug in the Y cable and to the power. And it recognises it is not charging when I unplug the power.

However, the OTG function itself is not powered unless the external power is in. Hmm.. Drat.

Its not clear the ‘echo …. aca’ matters. But for sure the CONFIG_USB_MSM_ACA does. However, it modifies almost no code… so it must pull in some other pre-req in the .config file (it seems to be CONFIG_USB_MSM_STANDARD_ACA, which changes the evaluation

The cable type shows up as PROPRIETARY_CHARGER / CHARGER_OTHER. Since I’m not setting the mA, this is leading to a 500mA charging (which is fine for my purpose).

from ACA, we want ID_A: “Configure device to act as host and don’t supply VBUS. In this state the device can charge as well.”. but i seem to end up in ID_B (idle + charge).

What happens is that it briefly comes up, applies power from the tablet out to the peripheral, then  removes power, causing the device to be removed.

The an7808 is used to detect cable presence etc.

The only difference in the MSM_STANDARD_ACA is:

/* RID_B and RID_C states does not exist with standard ACA */
#define phy_id_state_b(ints) 0
#define phy_id_state_c(ints) 0
#define phy_id_state_b(ints) (phy_id_state((ints)) == PHY_ID_B)
#define phy_id_state_c(ints) (phy_id_state((ints)) == PHY_ID_C)


so what is happening is that, on boot, plug in the otg cable (w/ nothing in it), it ends in state b_idle (should be a_wait_bcon). All goes great until it hits my:

ret = smb345_charger_enable(motg->chg_type == USB_INVALID_CHARGER ? false : true);

line (which is being passed false in this case). This somehow causes it to be thinking it should go to b_idle state.

So summary…

it can charge from OTG.

It can drive peripherals on OTG.

Its confused about doing both together, and something is wrong in the state machine in msm_otg.c driving this.

It needs ACA enabled to try to charge.

bq27541, smb345, pm8921 (all in drivers/powr) and ektf3k (drivers/input/touchscreen) seem to know when power state changes. The latter relates to slimport hdmi and receiving power through it.

drivers/usb/otg/msm_otg is the other spot the work happens

The BQ27541 is a ‘fuel-gauge’ and the data sheet shows it is not involved in decisions about charging. There is a callback there for cable-state change tho, bq27541_battery_callback(). This in turn calls power_supply_changed(). Not part of the charging, but notifies the system (to e.g. change scheduler etc).

CONFIG_PM8921_CHARGER is not set in kernel config. And i suspect there is a reason.

One can see the registers of the smb345 in real time through the sysfs:

cd /sys/bus/i2c/drivers/smb345/0-006a; cat reg_status

and there is no register difference between power in or power out when OTG cable is inserted.

I note that CONFIG_USB_MSM_ACA is not set. Will try setting it.

if I look @ the ACA (accessory charger adapter) in /sys/kernel/debug/msm_otg/aca, it is ‘disabled’. If i echo ‘enabled’ there, it doesn’t have any affect.

I would think that something like this would work (it does not):

diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 54603bb..bf2d872 100755
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -38,7 +38,12 @@
 #include <linux/usb/msm_hsusb.h>
 #include <linux/usb/msm_hsusb_hw.h>
 #include <linux/regulator/consumer.h>
+#include <linux/smb345-charger.h>
 #include <linux/mfd/pm8xxx/pm8921-charger.h>
 #include <linux/mfd/pm8xxx/misc.h>
 #include <linux/power_supply.h>
 #include <linux/mhl_8334.h>
@@ -97,6 +102,7 @@ static int global_id_pin_suspend_status;
 #define APQ_AP_ACOK 23
 #define APQ_OTG_ID_PIN 77

 enum msm_otg_smb345_chg_type {
@@ -106,6 +112,7 @@ enum msm_otg_smb345_chg_type {

 extern int usb_cable_type_detect(unsigned int chgr_type);
 extern void smb345_otg_status(bool on);
@@ -135,10 +142,12 @@ static void asus_chg_set_chg_mode(enum usb_chg_type chg_src)
 int chg_type = chg_src;

+#if 0 /* DAB */
 if (old_chg_type == chg_type) {
 printk(KERN_INFO "The USB charging type is same : return\n");

 switch (chg_type) {
@@ -1154,11 +1163,12 @@ psy_not_supported:

 static int msm_otg_notify_chg_type(struct msm_otg *motg)
- int charger_type;
+ int charger_type, ret;
 * Unify OTG driver charger types and power supply charger types
+ printk("DAB: [usb_otg] chg_type %d\r\n", motg->chg_type);

 if (motg->chg_type == USB_SDP_CHARGER)
 charger_type = POWER_SUPPLY_TYPE_USB;
@@ -1175,7 +1185,15 @@ static int msm_otg_notify_chg_type(struct msm_otg *motg)

- return pm8921_set_usb_power_supply_type(charger_type);
+ printk("DAB: call smb345_charger_enable(%d)", motg->chg_type == USB_INVALID_CHARGER ? false : true);
+ ret = smb345_charger_enable(motg->chg_type == USB_INVALID_CHARGER ? false : true);
+ printk("DAB: smb345_charger_enable() returned %d", ret);
+ ret = pm8921_set_usb_power_supply_type(charger_type);
+ printk("DAB: pm8921_set_usb_power_supply_type(%d) returned %d\r\n", charger_type, ret);
+ return ret;

 static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
@@ -1231,7 +1249,11 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)

 dev_info(motg->, "Avail curr from USB = %u\n", mA);

+ printk("DAB: not setting smb345 vbus_draw...");
 msm_otg_notify_power_supply(motg, mA);

 motg->cur_power = mA;

Would seem to be correct, but its not.