#define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */
#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */
-- -#define BQ27000_FLAG_CHGS BIT(7)
++ +#define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */
++ +#define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */
++ +#define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */
#define BQ27000_FLAG_FC BIT(5)
++ +#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */
++ +
++ +#define BQ27000_FLAGS_IMPORTANT (BQ27000_FLAG_FC|BQ27000_FLAG_CHGS|BIT(31))
#define BQ27500_REG_SOC 0x2C
#define BQ27500_REG_DCAP 0x3C /* Design capacity */
#define BQ27500_FLAG_DSC BIT(0)
++ +#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */
++ +#define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */
#define BQ27500_FLAG_FC BIT(9)
++ +#define BQ27500_FLAGS_IMPORTANT (BQ27500_FLAG_FC|BQ27500_FLAG_DSC|BIT(31))
++ +
#define BQ27000_RS 20 /* Resistor sense */
struct bq27x00_device_info;
int charge_full;
int cycle_count;
int capacity;
++ + int energy;
int flags;
-- -
-- - int current_now;
};
struct bq27x00_device_info {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
++ + POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true);
if (rsoc < 0)
-- - dev_err(di->dev, "error reading relative State-of-Charge\n");
++ + dev_dbg(di->dev, "error reading relative State-of-Charge\n");
return rsoc;
}
charge = bq27x00_read(di, reg, false);
if (charge < 0) {
-- - dev_err(di->dev, "error reading nominal available capacity\n");
++ + dev_dbg(di->dev, "error reading charge register %02x: %d\n",
++ + reg, charge);
return charge;
}
ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
if (ilmd < 0) {
-- - dev_err(di->dev, "error reading initial last measured discharge\n");
++ + dev_dbg(di->dev, "error reading initial last measured discharge\n");
return ilmd;
}
return ilmd;
}
++ +/*
++ + * Return the battery Available energy in µWh
++ + * Or < 0 if something fails.
++ + */
++ +static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)
++ +{
++ + int ae;
++ +
++ + ae = bq27x00_read(di, BQ27x00_REG_AE, false);
++ + if (ae < 0) {
++ + dev_dbg(di->dev, "error reading available energy\n");
++ + return ae;
++ + }
++ +
++ + if (di->chip == BQ27500)
++ + ae *= 1000;
++ + else
++ + ae = ae * 29200 / BQ27000_RS;
++ +
++ + return ae;
++ +}
++ +
++ +/*
++ + * Return the battery temperature in tenths of degree Celsius
++ + * Or < 0 if something fails.
++ + */
++ +static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
++ +{
++ + int temp;
++ +
++ + temp = bq27x00_read(di, BQ27x00_REG_TEMP, false);
++ + if (temp < 0) {
++ + dev_err(di->dev, "error reading temperature\n");
++ + return temp;
++ + }
++ +
++ + if (di->chip == BQ27500)
++ + temp -= 2731;
++ + else
++ + temp = ((temp * 5) - 5463) / 2;
++ +
++ + return temp;
++ +}
++ +
/*
* Return the battery Cycle count total
* Or < 0 if something fails.
tval = bq27x00_read(di, reg, false);
if (tval < 0) {
-- - dev_err(di->dev, "error reading register %02x: %d\n", reg, tval);
++ + dev_dbg(di->dev, "error reading time register %02x: %d\n",
++ + reg, tval);
return tval;
}
{
struct bq27x00_reg_cache cache = {0, };
bool is_bq27500 = di->chip == BQ27500;
++ + int flags_changed;
cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500);
if (cache.flags >= 0) {
-- - cache.capacity = bq27x00_battery_read_rsoc(di);
-- - cache.temperature = bq27x00_read(di, BQ27x00_REG_TEMP, false);
-- - cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE);
-- - cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);
-- - cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);
-- - cache.charge_full = bq27x00_battery_read_lmd(di);
++ + if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) {
++ + dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
++ + cache.capacity = -ENODATA;
++ + cache.energy = -ENODATA;
++ + cache.time_to_empty = -ENODATA;
++ + cache.time_to_empty_avg = -ENODATA;
++ + cache.time_to_full = -ENODATA;
++ + cache.charge_full = -ENODATA;
++ + } else {
++ + cache.capacity = bq27x00_battery_read_rsoc(di);
++ + cache.energy = bq27x00_battery_read_energy(di);
++ + cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE);
++ + cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);
++ + cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);
++ + cache.charge_full = bq27x00_battery_read_lmd(di);
++ + }
++ + cache.temperature = bq27x00_battery_read_temperature(di);
cache.cycle_count = bq27x00_battery_read_cyct(di);
-- - if (!is_bq27500)
-- - cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false);
-- -
/* We only have to read charge design full once */
if (di->charge_design_full <= 0)
di->charge_design_full = bq27x00_battery_read_ilmd(di);
}
-- - /* Ignore current_now which is a snapshot of the current battery state
-- - * and is likely to be different even between two consecutive reads */
-- - if (memcmp(&di->cache, &cache, sizeof(cache) - sizeof(int)) != 0) {
-- - di->cache = cache;
++ + flags_changed = di->cache.flags ^ cache.flags;
++ + di->cache = cache;
++ + if (is_bq27500)
++ + flags_changed &= BQ27500_FLAGS_IMPORTANT;
++ + else
++ + flags_changed &= BQ27000_FLAGS_IMPORTANT;
++ + if (flags_changed)
power_supply_changed(&di->bat);
-- - }
di->last_update = jiffies;
}
}
}
-- -
-- -/*
-- - * Return the battery temperature in tenths of degree Celsius
-- - * Or < 0 if something fails.
-- - */
-- -static int bq27x00_battery_temperature(struct bq27x00_device_info *di,
-- - union power_supply_propval *val)
-- -{
-- - if (di->cache.temperature < 0)
-- - return di->cache.temperature;
-- -
-- - if (di->chip == BQ27500)
-- - val->intval = di->cache.temperature - 2731;
-- - else
-- - val->intval = ((di->cache.temperature * 5) - 5463) / 2;
-- -
-- - return 0;
-- -}
-- -
/*
* Return the battery average current in µA
* Note that current can be negative signed as well
union power_supply_propval *val)
{
int curr;
++ + int flags;
-- - if (di->chip == BQ27500)
-- - curr = bq27x00_read(di, BQ27x00_REG_AI, false);
-- - else
-- - curr = di->cache.current_now;
-- -
-- - if (curr < 0)
++ + curr = bq27x00_read(di, BQ27x00_REG_AI, false);
++ + if (curr < 0) {
++ + dev_err(di->dev, "error reading current\n");
return curr;
++ + }
if (di->chip == BQ27500) {
/* bq27500 returns signed value */
val->intval = (int)((s16)curr) * 1000;
} else {
-- - if (di->cache.flags & BQ27000_FLAG_CHGS) {
++ + flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
++ + if (flags & BQ27000_FLAG_CHGS) {
dev_dbg(di->dev, "negative current!\n");
curr = -curr;
}
return 0;
}
-- -/*
-- - * Return the battery Voltage in milivolts
-- - * Or < 0 if something fails.
-- - */
-- -static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
++ +static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
union power_supply_propval *val)
{
-- - int volt;
++ + int level;
-- - volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
-- - if (volt < 0)
-- - return volt;
++ + if (di->chip == BQ27500) {
++ + if (di->cache.flags & BQ27500_FLAG_FC)
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
++ + else if (di->cache.flags & BQ27500_FLAG_SOC1)
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
++ + else if (di->cache.flags & BQ27500_FLAG_SOCF)
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
++ + else
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
++ + } else {
++ + if (di->cache.flags & BQ27000_FLAG_FC)
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
++ + else if (di->cache.flags & BQ27000_FLAG_EDV1)
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
++ + else if (di->cache.flags & BQ27000_FLAG_EDVF)
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
++ + else
++ + level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
++ + }
-- - val->intval = volt * 1000;
++ + val->intval = level;
return 0;
}
/*
-- - * Return the battery Available energy in µWh
++ + * Return the battery Voltage in milivolts
* Or < 0 if something fails.
*/
-- -static int bq27x00_battery_energy(struct bq27x00_device_info *di,
++ +static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
union power_supply_propval *val)
{
-- - int ae;
++ + int volt;
-- - ae = bq27x00_read(di, BQ27x00_REG_AE, false);
-- - if (ae < 0) {
-- - dev_err(di->dev, "error reading available energy\n");
-- - return ae;
++ + volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
++ + if (volt < 0) {
++ + dev_err(di->dev, "error reading voltage\n");
++ + return volt;
}
-- - if (di->chip == BQ27500)
-- - ae *= 1000;
-- - else
-- - ae = ae * 29200 / BQ27000_RS;
-- -
-- - val->intval = ae;
++ + val->intval = volt * 1000;
return 0;
}
-- -
static int bq27x00_simple_value(int value,
union power_supply_propval *val)
{
case POWER_SUPPLY_PROP_CAPACITY:
ret = bq27x00_simple_value(di->cache.capacity, val);
break;
++ + case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
++ + ret = bq27x00_battery_capacity_level(di, val);
++ + break;
case POWER_SUPPLY_PROP_TEMP:
-- - ret = bq27x00_battery_temperature(di, val);
++ + ret = bq27x00_simple_value(di->cache.temperature, val);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = bq27x00_simple_value(di->cache.time_to_empty, val);
ret = bq27x00_simple_value(di->cache.cycle_count, val);
break;
case POWER_SUPPLY_PROP_ENERGY_NOW:
-- - ret = bq27x00_battery_energy(di, val);
++ + ret = bq27x00_simple_value(di->cache.energy, val);
break;
default:
return -EINVAL;
struct bq27x00_device_info *di = to_bq27x00_device_info(psy);
cancel_delayed_work_sync(&di->work);
--- schedule_delayed_work(&di->work, 0);
+++ set_timer_slack(&di->work.timer, 2 * HZ);
+++ schedule_delayed_work(&di->work, 2 * HZ);
}
static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)
{
++ + /*
++ + * power_supply_unregister call bq27x00_battery_get_property which
++ + * call bq27x00_battery_poll.
++ + * Make sure that bq27x00_battery_poll will not call
++ + * schedule_delayed_work again after unregister (which cause OOPS).
++ + */
++ + poll_interval = 0;
++ +
cancel_delayed_work_sync(&di->work);
power_supply_unregister(&di->bat);
#include <linux/power_supply.h>
#include <linux/notifier.h>
#include <linux/usb/otg.h>
+++ #include <linux/ratelimit.h>
#define TWL4030_BCIMSTATEC 0x02
#define TWL4030_BCIICHG 0x08
#define TWL4030_MSTATEC_COMPLETE1 0x0b
#define TWL4030_MSTATEC_COMPLETE4 0x0e
--- static bool allow_usb;
+++ static bool allow_usb = 1;
module_param(allow_usb, bool, 0644);
MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
struct work_struct work;
int irq_chg;
int irq_bci;
+++ bool ac_charge_enable;
+++ bool usb_charge_enable;
unsigned long event;
+++ struct ratelimit_state ratelimit;
};
/*
static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
{
-- - return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0,
++ + return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear,
TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
TWL4030_PM_MASTER_BOOT_BCI);
}
}
/*
-- - * Enable/Disable USB Charge funtionality.
++ + * Enable/Disable USB Charge functionality.
*/
static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
{
int ret;
if (enable) {
+++ if (!bci->usb_charge_enable)
+++ return -EACCES;
+++
/* Check for USB charger conneted */
if (!twl4030_bci_have_vbus(bci))
return -ENODEV;
}
/* various monitoring events, for now we just log them here */
--- if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
+++ if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1) &&
+++ __ratelimit(&bci->ratelimit))
dev_warn(bci->dev, "battery temperature out of range\n");
--- if (irqs1 & TWL4030_BATSTS)
+++ if (irqs1 & TWL4030_BATSTS && __ratelimit(&bci->ratelimit))
dev_crit(bci->dev, "battery disconnected\n");
--- if (irqs2 & TWL4030_VBATOV)
+++ if (irqs2 & TWL4030_VBATOV && __ratelimit(&bci->ratelimit))
dev_crit(bci->dev, "VBAT overvoltage\n");
--- if (irqs2 & TWL4030_VBUSOV)
+++ if (irqs2 & TWL4030_VBUSOV && __ratelimit(&bci->ratelimit))
dev_crit(bci->dev, "VBUS overvoltage\n");
--- if (irqs2 & TWL4030_ACCHGOV)
+++ if (irqs2 & TWL4030_ACCHGOV && __ratelimit(&bci->ratelimit))
dev_crit(bci->dev, "Ac charger overvoltage\n");
+++ /* ack the interrupts */
+++ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, irqs1,
+++ TWL4030_INTERRUPTS_BCIISR1A);
+++ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, irqs2,
+++ TWL4030_INTERRUPTS_BCIISR2A);
+++
return IRQ_HANDLED;
}
return ret;
}
+++ static ssize_t twl4030_bci_ac_show_enable(struct device *dev,
+++ struct device_attribute *attr,
+++ char *buf)
+++ {
+++ u8 boot_bci;
+++ int ret;
+++
+++ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &boot_bci,
+++ TWL4030_PM_MASTER_BOOT_BCI);
+++ if (ret)
+++ return ret;
+++
+++ return sprintf(buf, "%d\n", (boot_bci & TWL4030_BCIAUTOAC) ? 1 : 0);
+++ }
+++
+++ static ssize_t twl4030_bci_ac_store_enable(struct device *dev,
+++ struct device_attribute *attr,
+++ const char *buf, size_t count)
+++ {
+++ struct power_supply *psy = dev_get_drvdata(dev);
+++ struct twl4030_bci *bci = container_of(psy, struct twl4030_bci, ac);
+++ unsigned long enable;
+++ int ret;
+++
+++ ret = strict_strtoul(buf, 10, &enable);
+++ if (ret || enable > 1)
+++ return -EINVAL;
+++
+++ bci->ac_charge_enable = enable;
+++ twl4030_charger_enable_ac(enable);
+++
+++ return count;
+++ }
+++ static const struct device_attribute dev_attr_enable_ac =
+++ __ATTR(enable, S_IRUGO | S_IWUSR, twl4030_bci_ac_show_enable,
+++ twl4030_bci_ac_store_enable);
+++
+++ static ssize_t twl4030_bci_usb_show_enable(struct device *dev,
+++ struct device_attribute *attr,
+++ char *buf)
+++ {
+++ u8 boot_bci;
+++ int ret;
+++
+++ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &boot_bci,
+++ TWL4030_PM_MASTER_BOOT_BCI);
+++ if (ret)
+++ return ret;
+++
+++ return sprintf(buf, "%d\n", (boot_bci & TWL4030_BCIAUTOUSB) ? 1 : 0);
+++ }
+++
+++ static ssize_t twl4030_bci_usb_store_enable(struct device *dev,
+++ struct device_attribute *attr,
+++ const char *buf, size_t count)
+++ {
+++ struct power_supply *psy = dev_get_drvdata(dev);
+++ struct twl4030_bci *bci = container_of(psy, struct twl4030_bci, usb);
+++ unsigned long enable;
+++ int ret;
+++
+++ ret = strict_strtoul(buf, 10, &enable);
+++ if (ret || enable > 1)
+++ return -EINVAL;
+++
+++ bci->usb_charge_enable = enable;
+++ twl4030_charger_enable_usb(bci, enable);
+++
+++ return count;
+++ }
+++ static const struct device_attribute dev_attr_enable_usb =
+++ __ATTR(enable, S_IRUGO | S_IWUSR, twl4030_bci_usb_show_enable,
+++ twl4030_bci_usb_store_enable);
+++
/*
* Returns the main charge FSM state
* Or < 0 on failure.
static int __init twl4030_bci_probe(struct platform_device *pdev)
{
+++ const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
struct twl4030_bci *bci;
int ret;
u32 reg;
+++ if (pdata == NULL) {
+++ dev_err(&pdev->dev, "No platform data\n");
+++ return -EINVAL;
+++ }
+++
bci = kzalloc(sizeof(*bci), GFP_KERNEL);
if (bci == NULL)
return -ENOMEM;
platform_set_drvdata(pdev, bci);
+++ ratelimit_state_init(&bci->ratelimit, HZ, 2);
+++
bci->ac.name = "twl4030_ac";
bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
bci->ac.properties = twl4030_charger_props;
bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props);
bci->ac.get_property = twl4030_bci_get_property;
+++ bci->ac.supplied_to = pdata->supplied_to;
+++ bci->ac.num_supplicants = pdata->num_supplicants;
ret = power_supply_register(&pdev->dev, &bci->ac);
if (ret) {
bci->usb.properties = twl4030_charger_props;
bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props);
bci->usb.get_property = twl4030_bci_get_property;
+++ bci->usb.supplied_to = pdata->supplied_to;
+++ bci->usb.num_supplicants = pdata->num_supplicants;
ret = power_supply_register(&pdev->dev, &bci->usb);
if (ret) {
otg_register_notifier(bci->transceiver, &bci->otg_nb);
}
+++ ret = device_create_file(bci->ac.dev, &dev_attr_enable_ac);
+++ if (ret) {
+++ dev_err(&pdev->dev, "failed to create sysfs file: %d\n", ret);
+++ goto fail_sysfs1;
+++ }
+++
+++ ret = device_create_file(bci->usb.dev, &dev_attr_enable_usb);
+++ if (ret) {
+++ dev_err(&pdev->dev, "failed to create sysfs file: %d\n", ret);
+++ goto fail_sysfs2;
+++ }
+++
/* Enable interrupts now. */
reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
TWL4030_TBATOR1 | TWL4030_BATSTS);
if (ret < 0)
dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
+++ bci->ac_charge_enable = true;
+++ bci->usb_charge_enable = true;
twl4030_charger_enable_ac(true);
twl4030_charger_enable_usb(bci, true);
return 0;
fail_unmask_interrupts:
+++ device_remove_file(bci->usb.dev, &dev_attr_enable_usb);
+++ fail_sysfs2:
+++ device_remove_file(bci->ac.dev, &dev_attr_enable_ac);
+++ fail_sysfs1:
if (bci->transceiver != NULL) {
otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
otg_put_transceiver(bci->transceiver);
{
struct twl4030_bci *bci = platform_get_drvdata(pdev);
+++ device_remove_file(bci->usb.dev, &dev_attr_enable_usb);
+++ device_remove_file(bci->ac.dev, &dev_attr_enable_ac);
+++
twl4030_charger_enable_ac(false);
twl4030_charger_enable_usb(bci, false);
musb->is_active = 0;
break;
}
+++
+++ switch (musb->xceiv->state) {
+++ case OTG_STATE_B_IDLE:
+++ case OTG_STATE_B_PERIPHERAL:
+++ cancel_delayed_work(&musb->vbus_workaround_work);
+++ schedule_delayed_work(&musb->vbus_workaround_work, HZ / 2);
+++ default:
+++ break;
+++ }
}
if (int_usb & MUSB_INTR_CONNECT) {
unsigned long flags;
pm_runtime_get_sync(musb->controller);
++ +
++ + musb_gadget_cleanup(musb);
++ +
spin_lock_irqsave(&musb->lock, flags);
musb_platform_disable(musb);
musb_generic_disable(musb);
musb_platform_exit(musb);
pm_runtime_put(musb->controller);
+++
+++ cancel_delayed_work(&musb->vbus_workaround_work);
+++
/* FIXME power down */
}
}
}
+++ #include <linux/usb/ulpi.h>
+++
+++ static void musb_vbus_workaround_work(struct work_struct *work)
+++ {
+++ struct musb *musb = container_of(work, struct musb, vbus_workaround_work.work);
+++ u8 devctl;
+++ int ret;
+++
+++ if (musb_ulpi_access.write == NULL)
+++ return;
+++
+++ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+++
+++ /*
+++ * I don't really know why but VBUS sometimes gets stuck and
+++ * causes session to never end. It would look like some pullup
+++ * is enabled when it shouldn't be on certain PHY states.
+++ * Turning on pulldowns magically drains VBUS to zero and allows
+++ * session to end, so let's do that here.
+++ *
+++ * XXX: probably better check VBUS on TWL?
+++ * beagle sometimes has session bit set but no VBUS on twl?
+++ */
+++ if ((musb->xceiv->state == OTG_STATE_B_PERIPHERAL ||
+++ musb->xceiv->state == OTG_STATE_B_IDLE) &&
+++ (devctl & MUSB_DEVCTL_VBUS) != (3 << MUSB_DEVCTL_VBUS_SHIFT) &&
+++ (devctl & MUSB_DEVCTL_VBUS) != (0 << MUSB_DEVCTL_VBUS_SHIFT)) {
+++ dev_dbg(musb->controller, "VBUS workaround..\n");
+++ ret = musb_ulpi_access.write(musb->xceiv, ULPI_SET(ULPI_OTG_CTRL),
+++ ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN);
+++ //if (ret)
+++ // dev_err(musb->controller, "VBUS workaround error\n");
+++ }
+++ }
+++
/* --------------------------------------------------------------------------
* Init support
*/
sysfs_remove_group(&musb->controller->kobj, &musb_attr_group);
#endif
-- - musb_gadget_cleanup(musb);
-- -
if (musb->nIrq >= 0) {
if (musb->irq_wake)
disable_irq_wake(musb->nIrq);
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
+++ INIT_DELAYED_WORK(&musb->vbus_workaround_work, musb_vbus_workaround_work);
+++
/* attach to the IRQ */
if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
dev_err(dev, "request_irq %d failed!\n", nIrq);
if (status < 0)
goto fail3;
++ + pm_runtime_put(musb->controller);
++ +
status = musb_init_debugfs(musb);
if (status < 0)
goto fail4;
* - Peripheral mode: peripheral is deactivated (or never-activated)
* - OTG mode: both roles are deactivated (or never-activated)
*/
-- - pm_runtime_get_sync(musb->controller);
musb_exit_debugfs(musb);
musb_shutdown(pdev);
-- - pm_runtime_put(musb->controller);
musb_free(musb);
iounmap(ctrl_base);
device_init_wakeup(&pdev->dev, 0);
if (!epio)
continue;
++ + musb_writeb(musb_base, MUSB_INDEX, i);
musb->context.index_regs[i].txmaxp =
musb_readw(epio, MUSB_TXMAXP);
musb->context.index_regs[i].txcsr =
if (!epio)
continue;
++ + musb_writeb(musb_base, MUSB_INDEX, i);
musb_writew(epio, MUSB_TXMAXP,
musb->context.index_regs[i].txmaxp);
musb_writew(epio, MUSB_TXCSR,
u8 index, testmode;
u8 devctl, busctl, misc;
++ + u32 otg_interfsel;
struct musb_csr_regs index_regs[MUSB_C_NUM_EPS];
};
#ifdef MUSB_CONFIG_PROC_FS
struct proc_dir_entry *proc_entry;
#endif
+++ struct delayed_work vbus_workaround_work;
};
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
struct twl4030_bci_platform_data {
int *battery_tmp_tbl;
unsigned int tblsize;
+++ char **supplied_to;
+++ size_t num_supplicants;
};
/* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */
unsigned num;
struct twl4030_resconfig *resource_config;
#define TWL4030_RESCONFIG_UNDEF ((u8)-1)
++ + bool use_poweroff; /* Board is wired for TWL poweroff */
};
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
extern int twl4030_remove_script(u8 flags);
++ +extern void twl4030_power_off(void);
struct twl4030_codec_data {
unsigned int digimic_delay; /* in ms */