diff options
| author | mattis fjallstrom <mattis@acm.org> | 2016-01-11 19:43:00 -0800 |
|---|---|---|
| committer | mattis fjallstrom <mattis@acm.org> | 2016-01-11 19:43:00 -0800 |
| commit | adacad29a479dff5c4959e11f59ed2623faa455a (patch) | |
| tree | e82e7daff2a96945997f2e249925ab37c2594001 | |
| parent | 9177138cf02829830c8d0003815bcff3c8c6b866 (diff) | |
| download | olio-linux-3.10-adacad29a479dff5c4959e11f59ed2623faa455a.tar.xz olio-linux-3.10-adacad29a479dff5c4959e11f59ed2623faa455a.zip | |
Enable/disable fuel gauge wake up depending on charge state. This turns off interrupts from the fuel gauge while charging.
Change-Id: I6a004c9d6ae9f47eae69387f27534f63c77ebfcf
| -rw-r--r-- | arch/arm/boot/dts/omap3_h1.dts | 26 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 2 | ||||
| -rw-r--r-- | drivers/power/bq27x00_battery.c | 225 |
3 files changed, 221 insertions, 32 deletions
diff --git a/arch/arm/boot/dts/omap3_h1.dts b/arch/arm/boot/dts/omap3_h1.dts index ee19b23f0cc..d162d465c8b 100644 --- a/arch/arm/boot/dts/omap3_h1.dts +++ b/arch/arm/boot/dts/omap3_h1.dts @@ -68,7 +68,7 @@ * for a gpio IRQ (banks numbered 1 to 6) * irq = 16 + 96 + (gpio_bank - 1) * 32 + GPIO no. */ - compatible = "ti,pad-wkup"; + compatible = "ti,pad-wkup"; /* Map the pad offset (off) to an interrupt (IRQ). */ /* if handle is 1, an irq will be generated based on */ @@ -83,10 +83,10 @@ /* off IRQ handle */ ti,pad_irq = <0x150 88 1>, /* 72 + 16, uart1 - BT input */ - <0x9f6 143 1>, /* mpu6515 irq pin - is this offset correct? */ - <0x9ea 122 1>, /* bq27400 chg irq8 */ - <0x9f4 123 1>, /* BT host wake */ - <0x1b0 23 1>; /* sys_nirq */ + <0x9f6 143 1>, /* mpu6515 irq pin - is this offset correct? */ + /* <0x9ea 122 1>, */ /* bq27400 chg irq8 - needed here? */ + <0x9f4 123 1>, /* BT host wake */ + <0x1b0 23 1>; /* sys_nirq */ }; sound { @@ -215,14 +215,14 @@ >; }; - dev_pins: pinmux_pv_pins { - pinctrl-single,pins = < - 0x5b2 0x204 /* USB control, ETK_D3, MODE4 | OUTPUT */ - /* 0x1a2 0x104 */ /* ALS interrupt, input, GPIO */ - 0x086 0x004 /* DRV2605 vibrator, output, GPIO */ - /* 0x938 0x104 */ /* Battery state, input, GPIO */ - >; - }; +dev_pins: pinmux_pv_pins { + pinctrl-single,pins = < + 0x5b2 0x204 /* USB control, ETK_D3, MODE4 | OUTPUT */ + /* 0x1a2 0x104 */ /* ALS interrupt, input, GPIO */ + 0x086 0x004 /* DRV2605 vibrator, output, GPIO */ + /* 0x938 0x104 */ /* Battery state, input, GPIO */ + >; + }; /* i2c1_pins: pinmux_i2c1_pins { diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 0e15ad357aa..00fb5ac811c 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -690,7 +690,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) r = omap_i2c_wait_for_bb(dev); /* If timeout, try to again check after attempting to clear the bus */ - if (WARN_ON(r == -ETIMEDOUT)) { + if (r == -ETIMEDOUT) { /* removed WARN_ON here, too much output from ts81001 */ r = omap_i2c_bus_clear(dev); if (r < 0) { dev_err(dev->dev, "Unable to recover i2c bus from bb\n"); diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 144e36bf07d..5835bc881cd 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -47,6 +47,19 @@ #include <linux/power/bq27x00_battery.h> #ifdef CONFIG_OLIO_TS81001 + +/* + * OLIO Model One needs the fuel gauge to talk to the charger in some + * instances, such as deciding whether we're using the wireless + * charger or not. Hence this build flag. + * + * In hindsight, a lot of this can probably be moved to the charger + * manager. + */ + +#include "asm/io.h" +#include "../../arch/arm/mach-omap2/mux.h" +#include "../../arch/arm/mach-omap2/iomap.h" #include "../staging/triune/ts81001.h" #endif /* CONFIG_OLIO_TS81001 */ @@ -58,7 +71,6 @@ #define olio_debug(format, ...) #endif - #define DRIVER_VERSION "1.2.0" #define INVALID_REG_ADDR 0xFF @@ -84,8 +96,14 @@ enum bq27xxx_reg_index { NUM_REGS }; +/* lock taken if we want the upper layers to run */ + struct wake_lock chg_wake_lock; +/* lock taken by interrupt to let work queue run*/ + +struct wake_lock isr_wake_lock; + /* bq27500 registers */ static __initdata u8 bq27500_regs[NUM_REGS] = { 0x00, /* CONTROL */ @@ -226,7 +244,7 @@ static __initdata u8 bq276xx_regs[NUM_REGS] = { #define BQ274XX_UNSEAL_KEY 0x80008000 #define BQ274XX_SOFT_RESET 0x43 -#define BQ274XX_FLAG_ITPOR 0x20 +#define BQ274XX_FLAG_ITPOR 0x20 #define BQ274XX_CTRL_STATUS_INITCOMP 0x80 #define BQ27XXX_FLAG_DSC BIT(0) @@ -306,6 +324,7 @@ struct bq27x00_device_info { unsigned long last_update; struct delayed_work work; + struct delayed_work interrupt_work; struct power_supply bat; @@ -410,6 +429,10 @@ static __initdata enum power_supply_property bq276xx_battery_props[] = { * Ordering the parameters based on subclass and then offset will help in * having fewer flash writes while updating. * Customize these values and, if necessary, add more based on system needs. + + To be added, maybe (for bq274xx): + {81, 9, 1, 3}, /* Quit Relax Time (3s) */ + */ static struct dm_reg bq274xx_dm_regs[] = { {36, 6, 1, 90}, /* FC Clear % */ @@ -447,6 +470,14 @@ static int read_dm_block(struct bq27x00_device_info *di, u8 subclass, u8 offset, u8 *data); +#ifdef CONFIG_OLIO_TS81001 +static int bq27x00_charger_status(struct bq27x00_device_info *di, + union power_supply_propval *val); +static int bq27x00_battery_status(struct bq27x00_device_info *di, + union power_supply_propval *val); +#endif + + /* * Common code for BQ27x00 devices */ @@ -1067,10 +1098,11 @@ static bool rom_mode_gauge_dm_initialized(struct bq27x00_device_info *di) dev_dbg(di->dev, "%s: flags - 0x%04x\n", __func__, flags); - if (flags & BQ274XX_FLAG_ITPOR) + if (flags & BQ274XX_FLAG_ITPOR) { return false; - else + } else { return true; + } } #define INITCOMP_TIMEOUT_MS 10000 @@ -1110,6 +1142,9 @@ static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di) subclass = dm_reg->subclass; offset = dm_reg->offset; + printk ("%s OLIO updating reg %d, offset %d\n", __FUNCTION__, + subclass, offset); + /* * Create a composite block number to see if the subsequent * register also belongs to the same 32 btye block in the DM @@ -1143,10 +1178,112 @@ static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di) exit_cfg_update_mode(di); } -static void bq27x00_battery_poll(struct work_struct *work) +/*************************************************************************** + * bq27x00_should_wakeup - decide whether to sleep or wake + * + * All I need is the power level last time we had an interrupt. If it isn't + * at least 2% from last time, go back to sleep. + * + * OK, that's not good enough. It needs to detect whether we're on or off + * the charger as well - use the status inquiry for that. + * + * I need to make a wrapper function around battery_poll that can be + * invoked when we're coming from an interrupt. That, or a global variable. + * Some way to only do this test when it's actually relevant. + */ + + +static bool bq27x00_should_wakeup(struct bq27x00_device_info *di) { + + static int previous_charge = 0; /* initial value */ + static int status_previous = 0; + int charge_diff; + int soc; + union power_supply_propval val; + + int status; + + printk ("%s OLIO entered\n", __FUNCTION__); + + /* check if power change is big enough for us to + * wake up to. + */ + + if ((soc = bq27x00_battery_read_soc(di)) < 0) + return false; /* error */ + + charge_diff = abs (soc - previous_charge); + + printk ("%s OLIO found diff %d\n", __FUNCTION__, charge_diff); + + if (charge_diff >= 5) { + previous_charge = soc; + printk ("exiting, true\n"); + return true; + } + + /* else check the status. This should turn off interrupts if + * it wasn't done before. + */ + + status = bq27x00_battery_status(di, &val); + + /* we ignore unknown statuses - it's probably just the ts81001 + * that hasn't woken up yet + */ + + if (status != POWER_SUPPLY_STATUS_UNKNOWN && + status != status_previous) { /* something has changed! */ + printk ("%s OLIO state changed (old = %d, new = %d, " + "wake up!\n", __FUNCTION__, status_previous, + status); + status_previous = status; + return true; + } else { + printk ("%s OLIO old state = %d, new state = %d, sleep\n", + __FUNCTION__, status_previous, status); + } + + printk ("exiting, false\n"); + + return false; +} + +/*************************************************************************** + * bq27x00_battery_interrupt - work to do from interrupt + * + * Decide whether we actually want to wake up, then take a wake lock if + * appropriate. + */ + +static void bq27x00_battery_interrupt (struct work_struct *i_work) +{ + int status; + union power_supply_propval val; + struct bq27x00_device_info *di = + container_of(i_work, struct bq27x00_device_info, interrupt_work.work); + + printk ("%s OLIO taking charge wake lock\n", __FUNCTION__); + + wake_lock_timeout(&chg_wake_lock, msecs_to_jiffies(500)); + bq27x00_update (di); + status = bq27x00_battery_status(di, &val); +#if 0 + if (bq27x00_should_wakeup(di)) { + + } +#endif + printk ("%s OLIO battery status = %d\n", __FUNCTION__, status); + + /* release the low-level wake lock (taken in isr handler) */ + wake_unlock (&isr_wake_lock); +} + + +static void bq27x00_battery_poll(struct work_struct *p_work) { struct bq27x00_device_info *di = - container_of(work, struct bq27x00_device_info, work.work); + container_of(p_work, struct bq27x00_device_info, work.work); if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) && !rom_mode_gauge_dm_initialized(di)) { @@ -1168,7 +1305,7 @@ static void bq27x00_battery_poll(struct work_struct *work) * Or 0 if something fails. */ static int bq27x00_battery_current(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { int curr; int flags; @@ -1197,13 +1334,39 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, #ifdef CONFIG_OLIO_TS81001 -static int bq27x00_charger_status(struct bq27x00_device_info *di, - union power_supply_propval *val) { +/* These are unfortunately hardcoded to our (OLIO's) GPIO pins right now. */ + +/* We need to turn off the wakeup capability of the pins when we don't + * want the interrupts, or all heck breaks loose. + +static void olio_interrupt_enabled (int irq) { + __raw_writew ((u16) (4 << 12 | 1 << 8 | 0 << 4 | 4), + (volatile void *) + OMAP2_L4_IO_ADDRESS(OMAP3_CONTROL_PADCONF_MUX_PBASE) + + OMAP3_CONTROL_PADCONF_SYS_CLKOUT1_OFFSET); + enable_irq (irq); + enable_irq_wake (irq); +} + + +static void olio_interrupt_disabled (int irq) { + disable_irq_wake (irq); + disable_irq (irq); + __raw_writew ((u16) (0 << 12 | 1 << 8 | 0 << 4 | 4), + (volatile void *) + OMAP2_L4_IO_ADDRESS(OMAP3_CONTROL_PADCONF_MUX_PBASE) + + OMAP3_CONTROL_PADCONF_SYS_CLKOUT1_OFFSET); +} + + +static volatile int irq_enabled = 1; /* enabled from start */ + +static int bq27x00_charger_status (struct bq27x00_device_info *di, + union power_supply_propval *val) { struct device * bq = di->dev; struct bq27x00_platform_data * pdata = bq->platform_data; struct ts81001 * ts81001; ts81001_state_t state; - int curr; olio_debug("entered\n"); @@ -1240,11 +1403,29 @@ static int bq27x00_charger_status(struct bq27x00_device_info *di, { case NOT_CHARGING: olio_debug("exiting, DISCHARGING\n"); + mutex_lock(&di->lock); + if (!irq_enabled) { + printk ("%s OLIO off charger, enabling chg interrupts\n", + __FUNCTION__); + irq_enabled = 1; + olio_interrupt_enabled (di->irq); + /* enable_irq_wake (di->irq); */ /* enable interrupts */ + } + mutex_unlock(&di->lock); return POWER_SUPPLY_STATUS_DISCHARGING; case PRECHARGE: case CHARGING_1C: case TOPOFF: olio_debug("exiting, CHARGING\n"); + mutex_lock(&di->lock); + if (irq_enabled) { + printk ("%s OLIO on charger, disabling chg interrupts\n", + __FUNCTION__); + irq_enabled = 0; + olio_interrupt_disabled (di->irq); + /* disable_irq_wake (di->irq); */ /* disable interrupts */ + } + mutex_unlock(&di->lock); return POWER_SUPPLY_STATUS_CHARGING; default: olio_debug("Couldn't read status from ts81001 charger.\n"); @@ -1252,7 +1433,6 @@ static int bq27x00_charger_status(struct bq27x00_device_info *di, return POWER_SUPPLY_STATUS_UNKNOWN; } } - #endif static int bq27x00_battery_status(struct bq27x00_device_info *di, @@ -1438,6 +1618,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, return ret; } + static void bq27x00_external_power_changed(struct power_supply *psy) { struct bq27x00_device_info *di = to_bq27x00_device_info(psy); @@ -1446,14 +1627,17 @@ static void bq27x00_external_power_changed(struct power_supply *psy) schedule_delayed_work(&di->work, 0); } -static irqreturn_t bq27x00_chg_isr(int irq, void *dev) { +static irqreturn_t bq27x00_chg_isr(int irq, void *dev) { struct power_supply *ps = (struct power_supply*) dev; - wake_lock_timeout(&chg_wake_lock, msecs_to_jiffies(500)); - /* dev_dbg(ps->dev, "%s: Updating battery status\n", __func__); Don't - do i/o in interrupt! */ + struct bq27x00_device_info *di = to_bq27x00_device_info(ps); + + printk ("%s OLIO ISR triggered!\n", __FUNCTION__); + + wake_lock(&isr_wake_lock); - bq27x00_external_power_changed(ps); + cancel_delayed_work(&di->interrupt_work); + schedule_delayed_work(&di->interrupt_work, 0); return IRQ_HANDLED; } @@ -1497,6 +1681,7 @@ static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di) di->bat.get_property = bq27x00_battery_get_property; di->bat.external_power_changed = bq27x00_external_power_changed; + INIT_DELAYED_WORK(&di->interrupt_work, bq27x00_battery_interrupt); INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); mutex_init(&di->lock); @@ -1510,6 +1695,7 @@ static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di) /* device_init_wakeup(di->dev, 1); */ + wake_lock_init(&isr_wake_lock, WAKE_LOCK_SUSPEND, "isr_wake_lock"); wake_lock_init(&chg_wake_lock, WAKE_LOCK_SUSPEND, "chg_wake_lock"); if(di->irq) { @@ -1549,6 +1735,7 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) poll_interval = 0; cancel_delayed_work_sync(&di->work); + cancel_delayed_work_sync(&di->interrupt_work); power_supply_unregister(&di->bat); @@ -1786,6 +1973,7 @@ static const struct attribute_group bq27x00_attr_group = { #ifdef CONFIG_OLIO_TS81001 + /*************************************************************************** * bq27x00_set_platform_data - act on basic platform data * @@ -1914,6 +2102,7 @@ static int __init bq27x00_battery_probe(struct i2c_client *client, goto batt_failed_3; /* Schedule a polling after about 1 min */ + schedule_delayed_work(&di->work, 60 * HZ); i2c_set_clientdata(client, di); @@ -1967,8 +2156,8 @@ static struct i2c_driver bq27x00_battery_driver = { .driver = { .name = "bq27x00-battery", }, - .probe = bq27x00_battery_probe, - .remove = bq27x00_battery_remove, + .probe = bq27x00_battery_probe, + .remove = bq27x00_battery_remove, .id_table = bq27x00_id, }; |