diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2011-06-28 20:34:02 +0530 |
---|---|---|
committer | Niket Sirsi <nsirsi@nvidia.com> | 2011-07-07 12:28:50 -0700 |
commit | a6bfb06cbcacec25078d79a24164ab9c8783070b (patch) | |
tree | 8153b5ee7af13244c53269eb230028659acb9c98 | |
parent | b4ed37082aeb30f00ccff63e60f4bf2d721c85af (diff) |
mfd: tps80031: Support second level of charge_control interrupt
There is multiple independent case for charge control interrupt and
so exposing each of the case as separate interrupt number.
bug 842072
Change-Id: I500d7e921e07b43de4eefdde2590f045022d8169
Reviewed-on: http://git-master/r/38732
Reviewed-by: Niket Sirsi <nsirsi@nvidia.com>
Tested-by: Niket Sirsi <nsirsi@nvidia.com>
-rw-r--r-- | drivers/mfd/tps80031.c | 172 | ||||
-rw-r--r-- | include/linux/mfd/tps80031.h | 9 |
2 files changed, 178 insertions, 3 deletions
diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index 39f7f2ee3149..0fa0b4d9c27f 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -44,6 +44,24 @@ #define TPS80031_INT_MSK_STS_B 0xD7 #define TPS80031_INT_MSK_STS_C 0xD8 +#define TPS80031_CONTROLLER_STAT1 0xE3 +#define CONTROLLER_STAT1_BAT_TEMP 0 +#define CONTROLLER_STAT1_BAT_REMOVED 1 +#define CONTROLLER_STAT1_VBUS_DET 2 +#define CONTROLLER_STAT1_VAC_DET 3 +#define CONTROLLER_STAT1_FAULT_WDG 4 +#define CONTROLLER_STAT1_LINCH_GATED 6 + +#define TPS80031_CONTROLLER_INT_MASK 0xE0 +#define CONTROLLER_INT_MASK_MVAC_DET 0 +#define CONTROLLER_INT_MASK_MVBUS_DET 1 +#define CONTROLLER_INT_MASK_MBAT_TEMP 2 +#define CONTROLLER_INT_MASK_MFAULT_WDG 3 +#define CONTROLLER_INT_MASK_MBAT_REMOVED 4 +#define CONTROLLER_INT_MASK_MLINCH_GATED 5 + +#define CHARGE_CONTROL_SUB_INT_MASK 0x3F + /* Version number related register */ #define TPS80031_JTAGVERNUM 0x87 @@ -78,15 +96,34 @@ static u8 pmc_ext_control_base[] = { struct tps80031_irq_data { u8 mask_reg; u8 mask_mask; + u8 is_sec_int; + u8 parent_int; + u8 mask_sec_int_reg; + u8 int_mask_bit; + u8 int_sec_sts_reg; + u8 int_sts_bit; }; -#define TPS80031_IRQ(_reg, _mask) \ +#define TPS80031_IRQ(_reg, _mask) \ { \ .mask_reg = (TPS80031_INT_MSK_LINE_##_reg) - \ TPS80031_INT_MSK_LINE_A, \ .mask_mask = (_mask), \ } +#define TPS80031_IRQ_SEC(_reg, _mask, _pint, _sint_mask_bit, _sint_sts_bit) \ + { \ + .mask_reg = (TPS80031_INT_MSK_LINE_##_reg) - \ + TPS80031_INT_MSK_LINE_A, \ + .mask_mask = (_mask), \ + .is_sec_int = true, \ + .parent_int = TPS80031_INT_##_pint, \ + .mask_sec_int_reg = TPS80031_CONTROLLER_INT_MASK, \ + .int_mask_bit = CONTROLLER_INT_MASK_##_sint_mask_bit, \ + .int_sec_sts_reg = TPS80031_CONTROLLER_STAT1, \ + .int_sts_bit = CONTROLLER_STAT1_##_sint_sts_bit \ + } + static const struct tps80031_irq_data tps80031_irqs[] = { [TPS80031_INT_PWRON] = TPS80031_IRQ(A, 0), @@ -113,6 +150,29 @@ static const struct tps80031_irq_data tps80031_irqs[] = { [TPS80031_INT_EXT_CHRG] = TPS80031_IRQ(C, 5), [TPS80031_INT_INT_CHRG] = TPS80031_IRQ(C, 6), [TPS80031_INT_RES2] = TPS80031_IRQ(C, 7), + [TPS80031_INT_BAT_TEMP_OVRANGE] = TPS80031_IRQ_SEC(C, 4, CHRG_CTRL, + MBAT_TEMP, BAT_TEMP), + [TPS80031_INT_BAT_REMOVED] = TPS80031_IRQ_SEC(C, 4, CHRG_CTRL, + MBAT_REMOVED, BAT_REMOVED), + [TPS80031_INT_VBUS_DET] = TPS80031_IRQ_SEC(C, 4, CHRG_CTRL, + MVBUS_DET, VBUS_DET), + [TPS80031_INT_VAC_DET] = TPS80031_IRQ_SEC(C, 4, CHRG_CTRL, + MVAC_DET, VAC_DET), + [TPS80031_INT_FAULT_WDG] = TPS80031_IRQ_SEC(C, 4, CHRG_CTRL, + MFAULT_WDG, FAULT_WDG), + [TPS80031_INT_LINCH_GATED] = TPS80031_IRQ_SEC(C, 4, CHRG_CTRL, + MLINCH_GATED, LINCH_GATED), +}; + +static const int controller_stat1_irq_nr[] = { + TPS80031_INT_BAT_TEMP_OVRANGE, + TPS80031_INT_BAT_REMOVED, + TPS80031_INT_VBUS_DET, + TPS80031_INT_VAC_DET, + TPS80031_INT_FAULT_WDG, + 0, + TPS80031_INT_LINCH_GATED, + 0 }; /* Structure for TPS80031 Slaves */ @@ -132,6 +192,10 @@ struct tps80031 { u32 irq_en; u8 mask_cache[3]; u8 mask_reg[3]; + u8 cont_int_mask_reg; + u8 cont_int_mask_cache; + u8 cont_int_en; + u8 prev_cont_stat1; struct tps80031_client tps_clients[TPS_NUM_SLAVES]; }; @@ -470,7 +534,14 @@ static void tps80031_irq_enable(unsigned int irq) unsigned int __irq = irq - tps80031->irq_base; const struct tps80031_irq_data *data = &tps80031_irqs[__irq]; - tps80031->mask_reg[data->mask_reg] &= ~(1 << data->mask_mask); + if (data->is_sec_int) { + tps80031->cont_int_mask_reg &= ~(1 << data->int_mask_bit); + tps80031->cont_int_en |= (1 << data->int_mask_bit); + tps80031->mask_reg[data->mask_reg] &= ~(1 << data->mask_mask); + tps80031->irq_en |= (1 << data->parent_int); + } else + tps80031->mask_reg[data->mask_reg] &= ~(1 << data->mask_mask); + tps80031->irq_en |= (1 << __irq); } @@ -481,7 +552,18 @@ static void tps80031_irq_disable(unsigned int irq) unsigned int __irq = irq - tps80031->irq_base; const struct tps80031_irq_data *data = &tps80031_irqs[__irq]; - tps80031->mask_reg[data->mask_reg] |= (1 << data->mask_mask); + if (data->is_sec_int) { + tps80031->cont_int_mask_reg |= (1 << data->int_mask_bit); + tps80031->cont_int_en &= ~(1 << data->int_mask_bit); + if (!tps80031->cont_int_en) { + tps80031->mask_reg[data->mask_reg] |= + (1 << data->mask_mask); + tps80031->irq_en &= ~(1 << data->parent_int); + } + tps80031->irq_en &= ~(1 << __irq); + } else + tps80031->mask_reg[data->mask_reg] |= (1 << data->mask_mask); + tps80031->irq_en &= ~(1 << __irq); } @@ -504,9 +586,57 @@ static void tps80031_irq_sync_unlock(unsigned int irq) } } + if (tps80031->cont_int_mask_reg != tps80031->cont_int_mask_cache) { + if (!WARN_ON(tps80031_write(tps80031->dev, SLAVE_ID2, + TPS80031_CONTROLLER_INT_MASK, + tps80031->cont_int_mask_reg))) + tps80031->cont_int_mask_cache = + tps80031->cont_int_mask_reg; + } + mutex_unlock(&tps80031->irq_lock); } +static irqreturn_t tps80031_charge_control_irq(int irq, void *data) +{ + struct tps80031 *tps80031 = data; + int ret = 0; + int i; + u8 cont_sts; + u8 org_sts; + + if (irq != (tps80031->irq_base + TPS80031_INT_CHRG_CTRL)) { + dev_err(tps80031->dev, "%s() Got the illegal interrupt %d\n", + __func__, irq); + return IRQ_NONE; + } + + ret = tps80031_read(tps80031->dev, SLAVE_ID2, + TPS80031_CONTROLLER_STAT1, &org_sts); + if (ret < 0) { + dev_err(tps80031->dev, "%s(): failed to read controller state1 " + "status %d\n", __func__, ret); + return IRQ_NONE; + } + + /* Get change from last interrupt and mask for interested interrupt + * for charge control interrupt */ + cont_sts = org_sts ^ tps80031->prev_cont_stat1; + tps80031->prev_cont_stat1 = org_sts; + cont_sts &= 0x5F; + + for (i = 0; i < 8; ++i) { + if (!controller_stat1_irq_nr[i]) + continue; + if ((cont_sts & BIT(i)) && + (tps80031->irq_en & BIT(controller_stat1_irq_nr[i]))) + handle_nested_irq(tps80031->irq_base + + controller_stat1_irq_nr[i]); + cont_sts &= ~BIT(i); + } + return IRQ_HANDLED; +} + static irqreturn_t tps80031_irq(int irq, void *data) { struct tps80031 *tps80031 = data; @@ -567,6 +697,35 @@ static int __devinit tps80031_irq_init(struct tps80031 *tps80031, int irq, TPS80031_INT_STS_A + i, 0xFF); } + ret = tps80031_read(tps80031->dev, SLAVE_ID2, + TPS80031_CONTROLLER_INT_MASK, + &tps80031->cont_int_mask_reg); + if (ret < 0) { + dev_err(tps80031->dev, "Error in reading the controller_mask " + "register %d\n", ret); + return ret; + } + + tps80031->cont_int_mask_reg |= CHARGE_CONTROL_SUB_INT_MASK; + tps80031->cont_int_mask_cache = tps80031->cont_int_mask_reg; + tps80031->cont_int_en = 0; + ret = tps80031_write(tps80031->dev, SLAVE_ID2, + TPS80031_CONTROLLER_INT_MASK, + tps80031->cont_int_mask_reg); + if (ret < 0) { + dev_err(tps80031->dev, "Error in writing the controller_mask " + "register %d\n", ret); + return ret; + } + + ret = tps80031_read(tps80031->dev, SLAVE_ID2, + TPS80031_CONTROLLER_STAT1, &tps80031->prev_cont_stat1); + if (ret < 0) { + dev_err(tps80031->dev, "%s(): failed to read controller state1 " + "status %d\n", __func__, ret); + return ret; + } + tps80031->irq_base = irq_base; tps80031->irq_chip.name = "tps80031"; @@ -588,7 +747,13 @@ static int __devinit tps80031_irq_init(struct tps80031 *tps80031, int irq, ret = request_threaded_irq(irq, NULL, tps80031_irq, IRQF_ONESHOT, "tps80031", tps80031); + /* register the isr for the secondary interrupt */ + if (!ret) + ret = request_threaded_irq(irq_base + TPS80031_INT_CHRG_CTRL, + NULL, tps80031_charge_control_irq, + IRQF_ONESHOT, "80031_chg_ctl", tps80031); if (!ret) { + device_init_wakeup(tps80031->dev, 1); enable_irq_wake(irq); } @@ -710,6 +875,7 @@ static int dbg_tps_show(struct seq_file *s, void *unused) print_regs("INT Regs", s, SLAVE_ID2, 0xD0, 0xD8); print_regs("PREQ Regs", s, SLAVE_ID1, 0xD7, 0xDF); print_regs("MASK_PH Regs", s, SLAVE_ID1, 0x20, 0x21); + print_regs("CONT_STATE", s, SLAVE_ID2, 0xE0, 0xE4); print_regs("VERNUM Regs", s, SLAVE_ID1, 0x87, 0x87); return 0; } diff --git a/include/linux/mfd/tps80031.h b/include/linux/mfd/tps80031.h index da5a6f62bf98..a5eb257919e0 100644 --- a/include/linux/mfd/tps80031.h +++ b/include/linux/mfd/tps80031.h @@ -72,6 +72,15 @@ enum { TPS80031_INT_EXT_CHRG, TPS80031_INT_INT_CHRG, TPS80031_INT_RES2, + TPS80031_INT_BAT_TEMP_OVRANGE, + TPS80031_INT_BAT_REMOVED, + TPS80031_INT_VBUS_DET, + TPS80031_INT_VAC_DET, + TPS80031_INT_FAULT_WDG, + TPS80031_INT_LINCH_GATED, + + /* Last interrupt id to get the end number */ + TPS80031_INT_END, }; enum { |