Merge branch 'for-mfd-and-power' of git://git.linaro.org/people/ljones/linux-3.0-ux500
From Lee Jones <lee.jones@linaro.org>: "Please find the next instalment of the AB8500 Power drivers upgrade. A lot of work has taken place on the internal development track, but little effort has gone into mainlining it. There is a large backlog of patches which are in need of forward-porting, then upstreaming. This patch-set aims to make a large dent into them." Conflicts: drivers/mfd/ab8500-gpadc.c
This commit is contained in:
commit
441a499e5a
|
@ -95,6 +95,7 @@
|
|||
#define AB8500_IT_MASK22_REG 0x55
|
||||
#define AB8500_IT_MASK23_REG 0x56
|
||||
#define AB8500_IT_MASK24_REG 0x57
|
||||
#define AB8500_IT_MASK25_REG 0x58
|
||||
|
||||
/*
|
||||
* latch hierarchy registers
|
||||
|
@ -102,15 +103,25 @@
|
|||
#define AB8500_IT_LATCHHIER1_REG 0x60
|
||||
#define AB8500_IT_LATCHHIER2_REG 0x61
|
||||
#define AB8500_IT_LATCHHIER3_REG 0x62
|
||||
#define AB8540_IT_LATCHHIER4_REG 0x63
|
||||
|
||||
#define AB8500_IT_LATCHHIER_NUM 3
|
||||
#define AB8540_IT_LATCHHIER_NUM 4
|
||||
|
||||
#define AB8500_REV_REG 0x80
|
||||
#define AB8500_IC_NAME_REG 0x82
|
||||
#define AB8500_SWITCH_OFF_STATUS 0x00
|
||||
|
||||
#define AB8500_TURN_ON_STATUS 0x00
|
||||
#define AB8505_TURN_ON_STATUS_2 0x04
|
||||
|
||||
#define AB8500_CH_USBCH_STAT1_REG 0x02
|
||||
#define VBUS_DET_DBNC100 0x02
|
||||
#define VBUS_DET_DBNC1 0x01
|
||||
|
||||
static DEFINE_SPINLOCK(on_stat_lock);
|
||||
static u8 turn_on_stat_mask = 0xFF;
|
||||
static u8 turn_on_stat_set;
|
||||
static bool no_bm; /* No battery management */
|
||||
module_param(no_bm, bool, S_IRUGO);
|
||||
|
||||
|
@ -130,9 +141,15 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
|
|||
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
|
||||
};
|
||||
|
||||
/* AB9540 support */
|
||||
/* AB9540 / AB8505 support */
|
||||
static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = {
|
||||
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24,
|
||||
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23
|
||||
};
|
||||
|
||||
/* AB8540 support */
|
||||
static const int ab8540_irq_regoffset[AB8540_NUM_IRQ_REGS] = {
|
||||
0, 1, 2, 3, 4, -1, -1, -1, -1, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23,
|
||||
25, 26, 27, 28, 29, 30, 31,
|
||||
};
|
||||
|
||||
static const char ab8500_version_str[][7] = {
|
||||
|
@ -352,6 +369,9 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
|
|||
is_ab8500_1p1_or_earlier(ab8500))
|
||||
continue;
|
||||
|
||||
if (ab8500->irq_reg_offset[i] < 0)
|
||||
continue;
|
||||
|
||||
ab8500->oldmask[i] = new;
|
||||
|
||||
reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
|
||||
|
@ -423,6 +443,18 @@ static struct irq_chip ab8500_irq_chip = {
|
|||
.irq_set_type = ab8500_irq_set_type,
|
||||
};
|
||||
|
||||
static void update_latch_offset(u8 *offset, int i)
|
||||
{
|
||||
/* Fix inconsistent ITFromLatch25 bit mapping... */
|
||||
if (unlikely(*offset == 17))
|
||||
*offset = 24;
|
||||
/* Fix inconsistent ab8540 bit mapping... */
|
||||
if (unlikely(*offset == 16))
|
||||
*offset = 25;
|
||||
if ((i==3) && (*offset >= 24))
|
||||
*offset += 2;
|
||||
}
|
||||
|
||||
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
|
||||
int latch_offset, u8 latch_val)
|
||||
{
|
||||
|
@ -474,9 +506,7 @@ static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500,
|
|||
latch_bit = __ffs(hier_val);
|
||||
latch_offset = (hier_offset << 3) + latch_bit;
|
||||
|
||||
/* Fix inconsistent ITFromLatch25 bit mapping... */
|
||||
if (unlikely(latch_offset == 17))
|
||||
latch_offset = 24;
|
||||
update_latch_offset(&latch_offset, hier_offset);
|
||||
|
||||
status = get_register_interruptible(ab8500,
|
||||
AB8500_INTERRUPT,
|
||||
|
@ -504,7 +534,7 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
|
|||
dev_vdbg(ab8500->dev, "interrupt\n");
|
||||
|
||||
/* Hierarchical interrupt version */
|
||||
for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) {
|
||||
for (i = 0; i < (ab8500->it_latchhier_num); i++) {
|
||||
int status;
|
||||
u8 hier_val;
|
||||
|
||||
|
@ -520,63 +550,6 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
|
||||
*
|
||||
* @ab8500: ab8500_irq controller to operate on.
|
||||
* @irq: index of the interrupt requested in the chip IRQs
|
||||
*
|
||||
* Useful for drivers to request their own IRQs.
|
||||
*/
|
||||
static int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
|
||||
{
|
||||
if (!ab8500)
|
||||
return -EINVAL;
|
||||
|
||||
return irq_create_mapping(ab8500->domain, irq);
|
||||
}
|
||||
|
||||
static irqreturn_t ab8500_irq(int irq, void *dev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev;
|
||||
int i;
|
||||
|
||||
dev_vdbg(ab8500->dev, "interrupt\n");
|
||||
|
||||
atomic_inc(&ab8500->transfer_ongoing);
|
||||
|
||||
for (i = 0; i < ab8500->mask_size; i++) {
|
||||
int regoffset = ab8500->irq_reg_offset[i];
|
||||
int status;
|
||||
u8 value;
|
||||
|
||||
/*
|
||||
* Interrupt register 12 doesn't exist prior to AB8500 version
|
||||
* 2.0
|
||||
*/
|
||||
if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
|
||||
continue;
|
||||
|
||||
status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
|
||||
AB8500_IT_LATCH1_REG + regoffset, &value);
|
||||
if (status < 0 || value == 0)
|
||||
continue;
|
||||
|
||||
do {
|
||||
int bit = __ffs(value);
|
||||
int line = i * 8 + bit;
|
||||
int virq = ab8500_irq_get_virq(ab8500, line);
|
||||
|
||||
handle_nested_irq(virq);
|
||||
ab8500_debug_register_interrupt(line);
|
||||
value &= ~(1 << bit);
|
||||
|
||||
} while (value);
|
||||
}
|
||||
atomic_dec(&ab8500->transfer_ongoing);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
|
@ -607,7 +580,9 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
|
|||
{
|
||||
int num_irqs;
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
if (is_ab8540(ab8500))
|
||||
num_irqs = AB8540_NR_IRQS;
|
||||
else if (is_ab9540(ab8500))
|
||||
num_irqs = AB9540_NR_IRQS;
|
||||
else if (is_ab8505(ab8500))
|
||||
num_irqs = AB8505_NR_IRQS;
|
||||
|
@ -650,6 +625,15 @@ static struct resource ab8500_gpadc_resources[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct resource ab8505_gpadc_resources[] = {
|
||||
{
|
||||
.name = "SW_CONV_END",
|
||||
.start = AB8500_INT_GP_SW_ADC_CONV_END,
|
||||
.end = AB8500_INT_GP_SW_ADC_CONV_END,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource ab8500_rtc_resources[] = {
|
||||
{
|
||||
.name = "60S",
|
||||
|
@ -973,6 +957,30 @@ static struct resource ab8505_iddet_resources[] = {
|
|||
.end = AB8505_INT_KEYSTUCK,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "VBUS_DET_R",
|
||||
.start = AB8500_INT_VBUS_DET_R,
|
||||
.end = AB8500_INT_VBUS_DET_R,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "VBUS_DET_F",
|
||||
.start = AB8500_INT_VBUS_DET_F,
|
||||
.end = AB8500_INT_VBUS_DET_F,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "ID_DET_PLUGR",
|
||||
.start = AB8500_INT_ID_DET_PLUGR,
|
||||
.end = AB8500_INT_ID_DET_PLUGR,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "ID_DET_PLUGF",
|
||||
.start = AB8500_INT_ID_DET_PLUGF,
|
||||
.end = AB8500_INT_ID_DET_PLUGF,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource ab8500_temp_resources[] = {
|
||||
|
@ -984,82 +992,6 @@ static struct resource ab8500_temp_resources[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell abx500_common_devs[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "ab8500-debug",
|
||||
.of_compatible = "stericsson,ab8500-debug",
|
||||
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
|
||||
.resources = ab8500_debug_resources,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
.of_compatible = "stericsson,ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
.of_compatible = "stericsson,ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.of_compatible = "stericsson,ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
|
||||
.resources = ab8500_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.of_compatible = "stericsson,ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.of_compatible = "stericsson,ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.of_compatible = "stericsson,ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 2,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 3,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-leds",
|
||||
.of_compatible = "stericsson,ab8500-leds",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-denc",
|
||||
.of_compatible = "stericsson,ab8500-denc",
|
||||
},
|
||||
{
|
||||
.name = "abx500-temp",
|
||||
.of_compatible = "stericsson,abx500-temp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
|
||||
.resources = ab8500_temp_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell ab8500_bm_devs[] = {
|
||||
{
|
||||
.name = "ab8500-charger",
|
||||
|
@ -1096,23 +1028,144 @@ static struct mfd_cell ab8500_bm_devs[] = {
|
|||
};
|
||||
|
||||
static struct mfd_cell ab8500_devs[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "pinctrl-ab8500",
|
||||
.name = "ab8500-debug",
|
||||
.of_compatible = "stericsson,ab8500-debug",
|
||||
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
|
||||
.resources = ab8500_debug_resources,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
.of_compatible = "stericsson,ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
.of_compatible = "stericsson,ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
|
||||
.resources = ab8500_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.of_compatible = "stericsson,ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.of_compatible = "stericsson,ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
|
||||
.name = "ab8500-poweron-key",
|
||||
.of_compatible = "stericsson,ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 2,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 3,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-leds",
|
||||
.of_compatible = "stericsson,ab8500-leds",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-denc",
|
||||
.of_compatible = "stericsson,ab8500-denc",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpio",
|
||||
.of_compatible = "stericsson,ab8500-gpio",
|
||||
},
|
||||
{
|
||||
.name = "abx500-temp",
|
||||
.of_compatible = "stericsson,abx500-temp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
|
||||
.resources = ab8500_temp_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-usb",
|
||||
.of_compatible = "stericsson,ab8500-usb",
|
||||
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
|
||||
.resources = ab8500_usb_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-codec",
|
||||
.of_compatible = "stericsson,ab8500-codec",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell ab9540_devs[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "ab8500-debug",
|
||||
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
|
||||
.resources = ab8500_debug_resources,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.of_compatible = "stericsson,ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
|
||||
.resources = ab8500_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-leds",
|
||||
},
|
||||
{
|
||||
.name = "abx500-temp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
|
||||
.resources = ab8500_temp_resources,
|
||||
},
|
||||
{
|
||||
.name = "pinctrl-ab9540",
|
||||
.of_compatible = "stericsson,ab9540-gpio",
|
||||
|
@ -1125,10 +1178,138 @@ static struct mfd_cell ab9540_devs[] = {
|
|||
{
|
||||
.name = "ab9540-codec",
|
||||
},
|
||||
{
|
||||
.name = "ab-iddet",
|
||||
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
|
||||
.resources = ab8505_iddet_resources,
|
||||
},
|
||||
};
|
||||
|
||||
/* Device list common to ab9540 and ab8505 */
|
||||
static struct mfd_cell ab9540_ab8505_devs[] = {
|
||||
/* Device list for ab8505 */
|
||||
static struct mfd_cell ab8505_devs[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "ab8500-debug",
|
||||
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
|
||||
.resources = ab8500_debug_resources,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
|
||||
.resources = ab8505_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-leds",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpio",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-usb",
|
||||
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
|
||||
.resources = ab8500_usb_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-codec",
|
||||
},
|
||||
{
|
||||
.name = "ab-iddet",
|
||||
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
|
||||
.resources = ab8505_iddet_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell ab8540_devs[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "ab8500-debug",
|
||||
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
|
||||
.resources = ab8500_debug_resources,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "abx500-clk",
|
||||
.of_compatible = "stericsson,abx500-clk",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
|
||||
.resources = ab8505_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-leds",
|
||||
},
|
||||
{
|
||||
.name = "abx500-temp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
|
||||
.resources = ab8500_temp_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpio",
|
||||
},
|
||||
{
|
||||
.name = "ab8540-usb",
|
||||
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
|
||||
.resources = ab8500_usb_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8540-codec",
|
||||
},
|
||||
{
|
||||
.name = "ab-iddet",
|
||||
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
|
||||
|
@ -1142,6 +1323,7 @@ static ssize_t show_chip_id(struct device *dev,
|
|||
struct ab8500 *ab8500;
|
||||
|
||||
ab8500 = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL);
|
||||
}
|
||||
|
||||
|
@ -1171,6 +1353,15 @@ static ssize_t show_switch_off_status(struct device *dev,
|
|||
return sprintf(buf, "%#x\n", value);
|
||||
}
|
||||
|
||||
/* use mask and set to override the register turn_on_stat value */
|
||||
void ab8500_override_turn_on_stat(u8 mask, u8 set)
|
||||
{
|
||||
spin_lock(&on_stat_lock);
|
||||
turn_on_stat_mask = mask;
|
||||
turn_on_stat_set = set;
|
||||
spin_unlock(&on_stat_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ab8500 has turned on due to (TURN_ON_STATUS):
|
||||
* 0x01 PORnVbat
|
||||
|
@ -1194,9 +1385,38 @@ static ssize_t show_turn_on_status(struct device *dev,
|
|||
AB8500_TURN_ON_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* In L9540, turn_on_status register is not updated correctly if
|
||||
* the device is rebooted with AC/USB charger connected. Due to
|
||||
* this, the device boots android instead of entering into charge
|
||||
* only mode. Read the AC/USB status register to detect the charger
|
||||
* presence and update the turn on status manually.
|
||||
*/
|
||||
if (is_ab9540(ab8500)) {
|
||||
spin_lock(&on_stat_lock);
|
||||
value = (value & turn_on_stat_mask) | turn_on_stat_set;
|
||||
spin_unlock(&on_stat_lock);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%#x\n", value);
|
||||
}
|
||||
|
||||
static ssize_t show_turn_on_status_2(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 value;
|
||||
struct ab8500 *ab8500;
|
||||
|
||||
ab8500 = dev_get_drvdata(dev);
|
||||
ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
|
||||
AB8505_TURN_ON_STATUS_2, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return sprintf(buf, "%#x\n", (value & 0x1));
|
||||
}
|
||||
|
||||
static ssize_t show_ab9540_dbbrstn(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -1253,6 +1473,7 @@ exit:
|
|||
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
|
||||
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
|
||||
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
|
||||
static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL);
|
||||
static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
|
||||
show_ab9540_dbbrstn, store_ab9540_dbbrstn);
|
||||
|
||||
|
@ -1263,6 +1484,11 @@ static struct attribute *ab8500_sysfs_entries[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *ab8505_sysfs_entries[] = {
|
||||
&dev_attr_turn_on_status_2.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *ab9540_sysfs_entries[] = {
|
||||
&dev_attr_chip_id.attr,
|
||||
&dev_attr_switch_off_status.attr,
|
||||
|
@ -1275,6 +1501,10 @@ static struct attribute_group ab8500_attr_group = {
|
|||
.attrs = ab8500_sysfs_entries,
|
||||
};
|
||||
|
||||
static struct attribute_group ab8505_attr_group = {
|
||||
.attrs = ab8505_sysfs_entries,
|
||||
};
|
||||
|
||||
static struct attribute_group ab9540_attr_group = {
|
||||
.attrs = ab9540_sysfs_entries,
|
||||
};
|
||||
|
@ -1290,6 +1520,15 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
"Battery level lower than power on reset threshold",
|
||||
"Power on key 1 pressed longer than 10 seconds",
|
||||
"DB8500 thermal shutdown"};
|
||||
static char *turn_on_status[] = {
|
||||
"Battery rising (Vbat)",
|
||||
"Power On Key 1 dbF",
|
||||
"Power On Key 2 dbF",
|
||||
"RTC Alarm",
|
||||
"Main Charger Detect",
|
||||
"Vbus Detect (USB)",
|
||||
"USB ID Detect",
|
||||
"UART Factory Mode Detect"};
|
||||
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
|
||||
|
@ -1351,13 +1590,20 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
ab8500->chip_id >> 4,
|
||||
ab8500->chip_id & 0x0F);
|
||||
|
||||
/* Configure AB8500 or AB9540 IRQ */
|
||||
if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
|
||||
/* Configure AB8540 */
|
||||
if (is_ab8540(ab8500)) {
|
||||
ab8500->mask_size = AB8540_NUM_IRQ_REGS;
|
||||
ab8500->irq_reg_offset = ab8540_irq_regoffset;
|
||||
ab8500->it_latchhier_num = AB8540_IT_LATCHHIER_NUM;
|
||||
}/* Configure AB8500 or AB9540 IRQ */
|
||||
else if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
|
||||
ab8500->mask_size = AB9540_NUM_IRQ_REGS;
|
||||
ab8500->irq_reg_offset = ab9540_irq_regoffset;
|
||||
ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
|
||||
} else {
|
||||
ab8500->mask_size = AB8500_NUM_IRQ_REGS;
|
||||
ab8500->irq_reg_offset = ab8500_irq_regoffset;
|
||||
ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
|
||||
}
|
||||
ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
|
||||
if (!ab8500->mask)
|
||||
|
@ -1396,10 +1642,36 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
} else {
|
||||
printk(KERN_CONT " None\n");
|
||||
}
|
||||
ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
|
||||
AB8500_TURN_ON_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev_info(ab8500->dev, "turn on reason(s) (%#x): ", value);
|
||||
|
||||
if (value) {
|
||||
for (i = 0; i < ARRAY_SIZE(turn_on_status); i++) {
|
||||
if (value & 1)
|
||||
printk("\"%s\" ", turn_on_status[i]);
|
||||
value = value >> 1;
|
||||
}
|
||||
printk("\n");
|
||||
} else {
|
||||
printk("None\n");
|
||||
}
|
||||
|
||||
if (plat && plat->init)
|
||||
plat->init(ab8500);
|
||||
|
||||
if (is_ab9540(ab8500)) {
|
||||
ret = get_register_interruptible(ab8500, AB8500_CHARGER,
|
||||
AB8500_CH_USBCH_STAT1_REG, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((value & VBUS_DET_DBNC1) && (value & VBUS_DET_DBNC100))
|
||||
ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
|
||||
AB8500_VBUS_DET);
|
||||
}
|
||||
|
||||
/* Clear and mask all interrupts */
|
||||
for (i = 0; i < ab8500->mask_size; i++) {
|
||||
/*
|
||||
|
@ -1410,6 +1682,9 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
is_ab8500_1p1_or_earlier(ab8500))
|
||||
continue;
|
||||
|
||||
if (ab8500->irq_reg_offset[i] < 0)
|
||||
continue;
|
||||
|
||||
get_register_interruptible(ab8500, AB8500_INTERRUPT,
|
||||
AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],
|
||||
&value);
|
||||
|
@ -1428,26 +1703,10 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Activate this feature only in ab9540 */
|
||||
/* till tests are done on ab8500 1p2 or later*/
|
||||
if (is_ab9540(ab8500)) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
|
||||
ab8500_hierarchical_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
}
|
||||
else {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
|
||||
ab8500_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
|
||||
ARRAY_SIZE(abx500_common_devs), NULL,
|
||||
ab8500->irq_base, ab8500->domain);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
|
||||
ab8500_hierarchical_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1455,6 +1714,14 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
|
||||
ARRAY_SIZE(ab9540_devs), NULL,
|
||||
ab8500->irq_base, ab8500->domain);
|
||||
else if (is_ab8540(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,
|
||||
ARRAY_SIZE(ab8540_devs), NULL,
|
||||
ab8500->irq_base, ab8500->domain);
|
||||
else if (is_ab8505(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,
|
||||
ARRAY_SIZE(ab8505_devs), NULL,
|
||||
ab8500->irq_base, ab8500->domain);
|
||||
else
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
|
||||
ARRAY_SIZE(ab8500_devs), NULL,
|
||||
|
@ -1462,13 +1729,6 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (is_ab9540(ab8500) || is_ab8505(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
|
||||
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
|
||||
ab8500->irq_base, ab8500->domain);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!no_bm) {
|
||||
/* Add battery management devices */
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
|
||||
|
@ -1478,12 +1738,19 @@ static int ab8500_probe(struct platform_device *pdev)
|
|||
dev_err(ab8500->dev, "error adding bm devices\n");
|
||||
}
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
|
||||
ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
|
||||
ret = sysfs_create_group(&ab8500->dev->kobj,
|
||||
&ab9540_attr_group);
|
||||
else
|
||||
ret = sysfs_create_group(&ab8500->dev->kobj,
|
||||
&ab8500_attr_group);
|
||||
|
||||
if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
|
||||
ab8500->chip_id >= AB8500_CUT2P0)
|
||||
ret = sysfs_create_group(&ab8500->dev->kobj,
|
||||
&ab8505_attr_group);
|
||||
|
||||
if (ret)
|
||||
dev_err(ab8500->dev, "error creating sysfs entries\n");
|
||||
|
||||
|
@ -1494,11 +1761,16 @@ static int ab8500_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct ab8500 *ab8500 = platform_get_drvdata(pdev);
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
|
||||
ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
|
||||
sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
|
||||
else
|
||||
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
|
||||
|
||||
if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
|
||||
ab8500->chip_id >= AB8500_CUT2P0)
|
||||
sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group);
|
||||
|
||||
mfd_remove_devices(ab8500->dev);
|
||||
|
||||
return 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,6 +37,13 @@
|
|||
#define AB8500_GPADC_AUTODATAL_REG 0x07
|
||||
#define AB8500_GPADC_AUTODATAH_REG 0x08
|
||||
#define AB8500_GPADC_MUX_CTRL_REG 0x09
|
||||
#define AB8540_GPADC_MANDATA2L_REG 0x09
|
||||
#define AB8540_GPADC_MANDATA2H_REG 0x0A
|
||||
#define AB8540_GPADC_APEAAX_REG 0x10
|
||||
#define AB8540_GPADC_APEAAT_REG 0x11
|
||||
#define AB8540_GPADC_APEAAM_REG 0x12
|
||||
#define AB8540_GPADC_APEAAH_REG 0x13
|
||||
#define AB8540_GPADC_APEAAL_REG 0x14
|
||||
|
||||
/*
|
||||
* OTP register offsets
|
||||
|
@ -49,19 +56,29 @@
|
|||
#define AB8500_GPADC_CAL_5 0x13
|
||||
#define AB8500_GPADC_CAL_6 0x14
|
||||
#define AB8500_GPADC_CAL_7 0x15
|
||||
/* New calibration for 8540 */
|
||||
#define AB8540_GPADC_OTP4_REG_7 0x38
|
||||
#define AB8540_GPADC_OTP4_REG_6 0x39
|
||||
#define AB8540_GPADC_OTP4_REG_5 0x3A
|
||||
|
||||
/* gpadc constants */
|
||||
#define EN_VINTCORE12 0x04
|
||||
#define EN_VTVOUT 0x02
|
||||
#define EN_GPADC 0x01
|
||||
#define DIS_GPADC 0x00
|
||||
#define SW_AVG_16 0x60
|
||||
#define AVG_1 0x00
|
||||
#define AVG_4 0x20
|
||||
#define AVG_8 0x40
|
||||
#define AVG_16 0x60
|
||||
#define ADC_SW_CONV 0x04
|
||||
#define EN_ICHAR 0x80
|
||||
#define BTEMP_PULL_UP 0x08
|
||||
#define EN_BUF 0x40
|
||||
#define DIS_ZERO 0x00
|
||||
#define GPADC_BUSY 0x01
|
||||
#define EN_FALLING 0x10
|
||||
#define EN_TRIG_EDGE 0x02
|
||||
#define EN_VBIAS_XTAL_TEMP 0x02
|
||||
|
||||
/* GPADC constants from AB8500 spec, UM0836 */
|
||||
#define ADC_RESOLUTION 1024
|
||||
|
@ -80,8 +97,21 @@
|
|||
#define ADC_CH_BKBAT_MIN 0
|
||||
#define ADC_CH_BKBAT_MAX 3200
|
||||
|
||||
/* GPADC constants from AB8540 spec */
|
||||
#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat*/
|
||||
#define ADC_CH_IBAT_MAX 6000
|
||||
#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat*/
|
||||
#define ADC_CH_IBAT_MAX_V 60
|
||||
#define IBAT_VDROP_L (-56) /* mV */
|
||||
#define IBAT_VDROP_H 56
|
||||
|
||||
/* This is used to not lose precision when dividing to get gain and offset */
|
||||
#define CALIB_SCALE 1000
|
||||
#define CALIB_SCALE 1000
|
||||
/*
|
||||
* Number of bits shift used to not lose precision
|
||||
* when dividing to get ibat gain.
|
||||
*/
|
||||
#define CALIB_SHIFT_IBAT 20
|
||||
|
||||
/* Time in ms before disabling regulator */
|
||||
#define GPADC_AUDOSUSPEND_DELAY 1
|
||||
|
@ -92,6 +122,7 @@ enum cal_channels {
|
|||
ADC_INPUT_VMAIN = 0,
|
||||
ADC_INPUT_BTEMP,
|
||||
ADC_INPUT_VBAT,
|
||||
ADC_INPUT_IBAT,
|
||||
NBR_CAL_INPUTS,
|
||||
};
|
||||
|
||||
|
@ -102,8 +133,10 @@ enum cal_channels {
|
|||
* @offset: Offset of the ADC channel
|
||||
*/
|
||||
struct adc_cal_data {
|
||||
u64 gain;
|
||||
u64 offset;
|
||||
s64 gain;
|
||||
s64 offset;
|
||||
u16 otp_calib_hi;
|
||||
u16 otp_calib_lo;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -116,7 +149,10 @@ struct adc_cal_data {
|
|||
* the completion of gpadc conversion
|
||||
* @ab8500_gpadc_lock: structure of type mutex
|
||||
* @regu: pointer to the struct regulator
|
||||
* @irq: interrupt number that is used by gpadc
|
||||
* @irq_sw: interrupt number that is used by gpadc for Sw
|
||||
* conversion
|
||||
* @irq_hw: interrupt number that is used by gpadc for Hw
|
||||
* conversion
|
||||
* @cal_data array of ADC calibration data structs
|
||||
*/
|
||||
struct ab8500_gpadc {
|
||||
|
@ -126,7 +162,8 @@ struct ab8500_gpadc {
|
|||
struct completion ab8500_gpadc_complete;
|
||||
struct mutex ab8500_gpadc_lock;
|
||||
struct regulator *regu;
|
||||
int irq;
|
||||
int irq_sw;
|
||||
int irq_hw;
|
||||
struct adc_cal_data cal_data[NBR_CAL_INPUTS];
|
||||
};
|
||||
|
||||
|
@ -171,6 +208,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
|
|||
gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
|
||||
break;
|
||||
|
||||
case XTAL_TEMP:
|
||||
case BAT_CTRL:
|
||||
case BTEMP_BALL:
|
||||
case ACC_DETECT1:
|
||||
|
@ -189,6 +227,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
|
|||
break;
|
||||
|
||||
case MAIN_BAT_V:
|
||||
case VBAT_TRUE_MEAS:
|
||||
/* For some reason we don't have calibrated data */
|
||||
if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
|
||||
res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
|
||||
|
@ -232,6 +271,20 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
|
|||
ADC_RESOLUTION;
|
||||
break;
|
||||
|
||||
case IBAT_VIRTUAL_CHANNEL:
|
||||
/* For some reason we don't have calibrated data */
|
||||
if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) {
|
||||
res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX -
|
||||
ADC_CH_IBAT_MIN) * ad_value /
|
||||
ADC_RESOLUTION;
|
||||
break;
|
||||
}
|
||||
/* Here we can use the calibrated data */
|
||||
res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain +
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].offset)
|
||||
>> CALIB_SHIFT_IBAT;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(gpadc->dev,
|
||||
"unknown channel, not possible to convert\n");
|
||||
|
@ -244,25 +297,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
|
|||
EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
|
||||
|
||||
/**
|
||||
* ab8500_gpadc_convert() - gpadc conversion
|
||||
* ab8500_gpadc_sw_hw_convert() - gpadc conversion
|
||||
* @channel: analog channel to be converted to digital data
|
||||
* @avg_sample: number of ADC sample to average
|
||||
* @trig_egde: selected ADC trig edge
|
||||
* @trig_timer: selected ADC trigger delay timer
|
||||
* @conv_type: selected conversion type (HW or SW conversion)
|
||||
*
|
||||
* This function converts the selected analog i/p to digital
|
||||
* data.
|
||||
*/
|
||||
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
|
||||
int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
|
||||
{
|
||||
int ad_value;
|
||||
int voltage;
|
||||
|
||||
ad_value = ab8500_gpadc_read_raw(gpadc, channel);
|
||||
if (ad_value < 0) {
|
||||
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
|
||||
ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
|
||||
trig_edge, trig_timer, conv_type);
|
||||
/* On failure retry a second time */
|
||||
if (ad_value < 0)
|
||||
ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
|
||||
trig_edge, trig_timer, conv_type);
|
||||
if (ad_value < 0) {
|
||||
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
|
||||
channel);
|
||||
return ad_value;
|
||||
}
|
||||
|
||||
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
|
||||
|
||||
if (voltage < 0)
|
||||
dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
|
||||
" %d AD: 0x%x\n", channel, ad_value);
|
||||
|
@ -274,21 +337,46 @@ EXPORT_SYMBOL(ab8500_gpadc_convert);
|
|||
/**
|
||||
* ab8500_gpadc_read_raw() - gpadc read
|
||||
* @channel: analog channel to be read
|
||||
* @avg_sample: number of ADC sample to average
|
||||
* @trig_edge: selected trig edge
|
||||
* @trig_timer: selected ADC trigger delay timer
|
||||
* @conv_type: selected conversion type (HW or SW conversion)
|
||||
*
|
||||
* This function obtains the raw ADC value, this then needs
|
||||
* to be converted by calling ab8500_gpadc_ad_to_voltage()
|
||||
* This function obtains the raw ADC value for an hardware conversion,
|
||||
* this then needs to be converted by calling ab8500_gpadc_ad_to_voltage()
|
||||
*/
|
||||
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
||||
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
|
||||
{
|
||||
int raw_data;
|
||||
raw_data = ab8500_gpadc_double_read_raw(gpadc, channel,
|
||||
avg_sample, trig_edge, trig_timer, conv_type, NULL);
|
||||
return raw_data;
|
||||
}
|
||||
|
||||
int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
|
||||
int *ibat)
|
||||
{
|
||||
int ret;
|
||||
int looplimit = 0;
|
||||
u8 val, low_data, high_data;
|
||||
unsigned long completion_timeout;
|
||||
u8 val, low_data, high_data, low_data2, high_data2;
|
||||
u8 val_reg1 = 0;
|
||||
unsigned int delay_min = 0;
|
||||
unsigned int delay_max = 0;
|
||||
u8 data_low_addr, data_high_addr;
|
||||
|
||||
if (!gpadc)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&gpadc->ab8500_gpadc_lock);
|
||||
/* check if convertion is supported */
|
||||
if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW))
|
||||
return -ENOTSUPP;
|
||||
if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW))
|
||||
return -ENOTSUPP;
|
||||
|
||||
mutex_lock(&gpadc->ab8500_gpadc_lock);
|
||||
/* Enable VTVout LDO this is required for GPADC */
|
||||
pm_runtime_get_sync(gpadc->dev);
|
||||
|
||||
|
@ -309,16 +397,34 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
}
|
||||
|
||||
/* Enable GPADC */
|
||||
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
|
||||
goto out;
|
||||
val_reg1 |= EN_GPADC;
|
||||
|
||||
/* Select the channel source and set average samples */
|
||||
switch (avg_sample) {
|
||||
case SAMPLE_1:
|
||||
val = channel | AVG_1;
|
||||
break;
|
||||
case SAMPLE_4:
|
||||
val = channel | AVG_4;
|
||||
break;
|
||||
case SAMPLE_8:
|
||||
val = channel | AVG_8;
|
||||
break;
|
||||
default:
|
||||
val = channel | AVG_16;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Select the channel source and set average samples to 16 */
|
||||
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
|
||||
if (conv_type == ADC_HW) {
|
||||
ret = abx500_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val);
|
||||
val_reg1 |= EN_TRIG_EDGE;
|
||||
if (trig_edge)
|
||||
val_reg1 |= EN_FALLING;
|
||||
}
|
||||
else
|
||||
ret = abx500_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: set avg samples failed\n");
|
||||
|
@ -333,71 +439,129 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
switch (channel) {
|
||||
case MAIN_CHARGER_C:
|
||||
case USB_CHARGER_C:
|
||||
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
|
||||
EN_BUF | EN_ICHAR,
|
||||
EN_BUF | EN_ICHAR);
|
||||
val_reg1 |= EN_BUF | EN_ICHAR;
|
||||
break;
|
||||
case BTEMP_BALL:
|
||||
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
|
||||
/* Turn on btemp pull-up on ABB 3.0 */
|
||||
ret = abx500_mask_and_set_register_interruptible(
|
||||
gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
|
||||
EN_BUF | BTEMP_PULL_UP,
|
||||
EN_BUF | BTEMP_PULL_UP);
|
||||
|
||||
/*
|
||||
* Delay might be needed for ABB8500 cut 3.0, if not, remove
|
||||
* when hardware will be available
|
||||
*/
|
||||
usleep_range(1000, 1000);
|
||||
val_reg1 |= EN_BUF | BTEMP_PULL_UP;
|
||||
/*
|
||||
* Delay might be needed for ABB8500 cut 3.0, if not,
|
||||
* remove when hardware will be availible
|
||||
*/
|
||||
delay_min = 1000; /* Delay in micro seconds */
|
||||
delay_max = 10000; /* large range to optimise sleep mode */
|
||||
break;
|
||||
}
|
||||
/* Intentional fallthrough */
|
||||
default:
|
||||
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
|
||||
val_reg1 |= EN_BUF;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write configuration to register */
|
||||
ret = abx500_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: select falling edge failed\n");
|
||||
"gpadc_conversion: set Control register failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: start s/w conversion failed\n");
|
||||
goto out;
|
||||
if (delay_min != 0)
|
||||
usleep_range(delay_min, delay_max);
|
||||
|
||||
if (conv_type == ADC_HW) {
|
||||
/* Set trigger delay timer */
|
||||
ret = abx500_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: trig timer failed\n");
|
||||
goto out;
|
||||
}
|
||||
completion_timeout = 2 * HZ;
|
||||
data_low_addr = AB8500_GPADC_AUTODATAL_REG;
|
||||
data_high_addr = AB8500_GPADC_AUTODATAH_REG;
|
||||
} else {
|
||||
/* Start SW conversion */
|
||||
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
|
||||
ADC_SW_CONV, ADC_SW_CONV);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: start s/w conv failed\n");
|
||||
goto out;
|
||||
}
|
||||
completion_timeout = msecs_to_jiffies(CONVERSION_TIME);
|
||||
data_low_addr = AB8500_GPADC_MANDATAL_REG;
|
||||
data_high_addr = AB8500_GPADC_MANDATAH_REG;
|
||||
}
|
||||
|
||||
/* wait for completion of conversion */
|
||||
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
|
||||
msecs_to_jiffies(CONVERSION_TIME))) {
|
||||
completion_timeout)) {
|
||||
dev_err(gpadc->dev,
|
||||
"timeout: didn't receive GPADC conversion interrupt\n");
|
||||
"timeout didn't receive GPADC conv interrupt\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read the converted RAW data */
|
||||
ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_MANDATAL_REG, &low_data);
|
||||
ret = abx500_get_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, data_low_addr, &low_data);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_MANDATAH_REG, &high_data);
|
||||
ret = abx500_get_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, data_high_addr, &high_data);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: read high data failed\n");
|
||||
dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if double convertion is required */
|
||||
if ((channel == BAT_CTRL_AND_IBAT) ||
|
||||
(channel == VBAT_MEAS_AND_IBAT) ||
|
||||
(channel == VBAT_TRUE_MEAS_AND_IBAT) ||
|
||||
(channel == BAT_TEMP_AND_IBAT)) {
|
||||
|
||||
if (conv_type == ADC_HW) {
|
||||
/* not supported */
|
||||
ret = -ENOTSUPP;
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: only SW double conversion supported\n");
|
||||
goto out;
|
||||
} else {
|
||||
/* Read the converted RAW data 2 */
|
||||
ret = abx500_get_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
|
||||
&low_data2);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: read sw low data 2 failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = abx500_get_register_interruptible(gpadc->dev,
|
||||
AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
|
||||
&high_data2);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: read sw high data 2 failed\n");
|
||||
goto out;
|
||||
}
|
||||
if (ibat != NULL) {
|
||||
*ibat = (high_data2 << 8) | low_data2;
|
||||
} else {
|
||||
dev_warn(gpadc->dev,
|
||||
"gpadc_conversion: ibat not stored\n");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable GPADC */
|
||||
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
|
||||
|
@ -406,6 +570,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Disable VTVout LDO this is required for GPADC */
|
||||
pm_runtime_mark_last_busy(gpadc->dev);
|
||||
pm_runtime_put_autosuspend(gpadc->dev);
|
||||
|
||||
|
@ -422,9 +587,7 @@ out:
|
|||
*/
|
||||
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
|
||||
|
||||
pm_runtime_put(gpadc->dev);
|
||||
|
||||
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
|
||||
|
@ -433,16 +596,16 @@ out:
|
|||
EXPORT_SYMBOL(ab8500_gpadc_read_raw);
|
||||
|
||||
/**
|
||||
* ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
|
||||
* ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
|
||||
* @irq: irq number
|
||||
* @data: pointer to the data passed during request irq
|
||||
*
|
||||
* This is a interrupt service routine for s/w gpadc conversion completion.
|
||||
* This is a interrupt service routine for gpadc conversion completion.
|
||||
* Notifies the gpadc completion is completed and the converted raw value
|
||||
* can be read from the registers.
|
||||
* Returns IRQ status(IRQ_HANDLED)
|
||||
*/
|
||||
static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
|
||||
static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc)
|
||||
{
|
||||
struct ab8500_gpadc *gpadc = _gpadc;
|
||||
|
||||
|
@ -461,15 +624,27 @@ static int otp_cal_regs[] = {
|
|||
AB8500_GPADC_CAL_7,
|
||||
};
|
||||
|
||||
static int otp4_cal_regs[] = {
|
||||
AB8540_GPADC_OTP4_REG_7,
|
||||
AB8540_GPADC_OTP4_REG_6,
|
||||
AB8540_GPADC_OTP4_REG_5,
|
||||
};
|
||||
|
||||
static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
||||
{
|
||||
int i;
|
||||
int ret[ARRAY_SIZE(otp_cal_regs)];
|
||||
u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
|
||||
|
||||
int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
|
||||
u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)];
|
||||
int vmain_high, vmain_low;
|
||||
int btemp_high, btemp_low;
|
||||
int vbat_high, vbat_low;
|
||||
int ibat_high, ibat_low;
|
||||
s64 V_gain, V_offset, V2A_gain, V2A_offset;
|
||||
struct ab8500 *ab8500;
|
||||
|
||||
ab8500 = gpadc->parent;
|
||||
|
||||
/* First we read all OTP registers and store the error code */
|
||||
for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
|
||||
|
@ -489,7 +664,7 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
|||
* bt_h/l = btemp_high/low
|
||||
* vb_h/l = vbat_high/low
|
||||
*
|
||||
* Data bits:
|
||||
* Data bits 8500/9540:
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | | vm_h9 | vm_h8
|
||||
|
@ -507,6 +682,35 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
|||
* | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
*
|
||||
* Data bits 8540:
|
||||
* OTP2
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* |
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
*
|
||||
* Data bits 8540:
|
||||
* OTP4
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | | ib_h9 | ib_h8 | ib_h7
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5
|
||||
* |.......|.......|.......|.......|.......|.......|.......|.......
|
||||
* | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 |
|
||||
*
|
||||
*
|
||||
* Ideal output ADC codes corresponding to injected input voltages
|
||||
* during manufacturing is:
|
||||
|
@ -519,38 +723,116 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
|||
* vbat_low: Vin = 2380mV / ADC ideal code = 33
|
||||
*/
|
||||
|
||||
/* Calculate gain and offset for VMAIN if all reads succeeded */
|
||||
if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
|
||||
vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
|
||||
((gpadc_cal[1] & 0x3F) << 2) |
|
||||
((gpadc_cal[2] & 0xC0) >> 6));
|
||||
if (is_ab8540(ab8500)) {
|
||||
/* Calculate gain and offset for VMAIN if all reads succeeded*/
|
||||
if (!(ret[1] < 0 || ret[2] < 0)) {
|
||||
vmain_high = (((gpadc_cal[1] & 0xFF) << 2) |
|
||||
((gpadc_cal[2] & 0xC0) >> 6));
|
||||
vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
|
||||
|
||||
vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
|
||||
(u16)vmain_high;
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
|
||||
(u16)vmain_low;
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
|
||||
(19500 - 315) / (vmain_high - vmain_low);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
|
||||
(CALIB_SCALE * (19500 - 315) /
|
||||
(vmain_high - vmain_low)) * vmain_high;
|
||||
} else {
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
|
||||
(19500 - 315) / (vmain_high - vmain_low);
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
|
||||
19500 - (CALIB_SCALE * (19500 - 315) /
|
||||
(vmain_high - vmain_low)) * vmain_high;
|
||||
} else {
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
|
||||
}
|
||||
|
||||
/* Read IBAT calibration Data */
|
||||
for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) {
|
||||
ret_otp4[i] = abx500_get_register_interruptible(
|
||||
gpadc->dev, AB8500_OTP_EMUL,
|
||||
otp4_cal_regs[i], &gpadc_otp4[i]);
|
||||
if (ret_otp4[i] < 0)
|
||||
dev_err(gpadc->dev,
|
||||
"%s: read otp4 reg 0x%02x failed\n",
|
||||
__func__, otp4_cal_regs[i]);
|
||||
}
|
||||
|
||||
/* Calculate gain and offset for IBAT if all reads succeeded */
|
||||
if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) {
|
||||
ibat_high = (((gpadc_otp4[0] & 0x07) << 7) |
|
||||
((gpadc_otp4[1] & 0xFE) >> 1));
|
||||
ibat_low = (((gpadc_otp4[1] & 0x01) << 5) |
|
||||
((gpadc_otp4[2] & 0xF8) >> 3));
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi =
|
||||
(u16)ibat_high;
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo =
|
||||
(u16)ibat_low;
|
||||
|
||||
V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L)
|
||||
<< CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
|
||||
|
||||
V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) -
|
||||
(((IBAT_VDROP_H - IBAT_VDROP_L) <<
|
||||
CALIB_SHIFT_IBAT) / (ibat_high - ibat_low))
|
||||
* ibat_high;
|
||||
/*
|
||||
* Result obtained is in mV (at a scale factor),
|
||||
* we need to calculate gain and offset to get mA
|
||||
*/
|
||||
V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/
|
||||
(ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
|
||||
V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN -
|
||||
ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V)
|
||||
<< CALIB_SHIFT_IBAT)
|
||||
/ (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain;
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset *
|
||||
V2A_gain + V2A_offset;
|
||||
} else {
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].gain = 0;
|
||||
}
|
||||
|
||||
dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n",
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].gain,
|
||||
gpadc->cal_data[ADC_INPUT_IBAT].offset);
|
||||
} else {
|
||||
/* Calculate gain and offset for VMAIN if all reads succeeded */
|
||||
if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
|
||||
vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
|
||||
((gpadc_cal[1] & 0x3F) << 2) |
|
||||
((gpadc_cal[2] & 0xC0) >> 6));
|
||||
vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
|
||||
(u16)vmain_high;
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
|
||||
(u16)vmain_low;
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
|
||||
(19500 - 315) / (vmain_high - vmain_low);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
|
||||
19500 - (CALIB_SCALE * (19500 - 315) /
|
||||
(vmain_high - vmain_low)) * vmain_high;
|
||||
} else {
|
||||
gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate gain and offset for BTEMP if all reads succeeded */
|
||||
if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
|
||||
btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
|
||||
(gpadc_cal[3] << 1) |
|
||||
((gpadc_cal[4] & 0x80) >> 7));
|
||||
|
||||
(gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7));
|
||||
btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high;
|
||||
gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low;
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_BTEMP].gain =
|
||||
CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
|
||||
(CALIB_SCALE * (1300 - 21) /
|
||||
(btemp_high - btemp_low)) * btemp_high;
|
||||
(CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
|
||||
* btemp_high;
|
||||
} else {
|
||||
gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
|
||||
}
|
||||
|
@ -560,9 +842,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
|||
vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
|
||||
vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high;
|
||||
gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low;
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
|
||||
(4700 - 2380) / (vbat_high - vbat_low);
|
||||
|
||||
gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
|
||||
(CALIB_SCALE * (4700 - 2380) /
|
||||
(vbat_high - vbat_low)) * vbat_high;
|
||||
|
@ -608,6 +892,31 @@ static int ab8500_gpadc_runtime_idle(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_suspend(struct device *dev)
|
||||
{
|
||||
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&gpadc->ab8500_gpadc_lock);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
regulator_disable(gpadc->regu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_resume(struct device *dev)
|
||||
{
|
||||
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
|
||||
|
||||
regulator_enable(gpadc->regu);
|
||||
|
||||
pm_runtime_mark_last_busy(gpadc->dev);
|
||||
pm_runtime_put_autosuspend(gpadc->dev);
|
||||
|
||||
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -619,13 +928,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
|
||||
if (gpadc->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get platform irq-%d\n",
|
||||
gpadc->irq);
|
||||
ret = gpadc->irq;
|
||||
goto fail;
|
||||
}
|
||||
gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
|
||||
if (gpadc->irq_sw < 0)
|
||||
dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n");
|
||||
|
||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||
if (gpadc->irq_hw < 0)
|
||||
dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n");
|
||||
|
||||
gpadc->dev = &pdev->dev;
|
||||
gpadc->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
|
@ -634,15 +943,31 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
/* Initialize completion used to notify completion of conversion */
|
||||
init_completion(&gpadc->ab8500_gpadc_complete);
|
||||
|
||||
/* Register interrupt - SwAdcComplete */
|
||||
ret = request_threaded_irq(gpadc->irq, NULL,
|
||||
ab8500_bm_gpswadcconvend_handler,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"ab8500-gpadc", gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
|
||||
gpadc->irq);
|
||||
goto fail;
|
||||
/* Register interrupts */
|
||||
if (gpadc->irq_sw >= 0) {
|
||||
ret = request_threaded_irq(gpadc->irq_sw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
|
||||
gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"Failed to register interrupt irq: %d\n",
|
||||
gpadc->irq_sw);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpadc->irq_hw >= 0) {
|
||||
ret = request_threaded_irq(gpadc->irq_hw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
|
||||
gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"Failed to register interrupt irq: %d\n",
|
||||
gpadc->irq_hw);
|
||||
goto fail_irq;
|
||||
}
|
||||
}
|
||||
|
||||
/* VTVout LDO used to power up ab8500-GPADC */
|
||||
|
@ -669,11 +994,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
ab8500_gpadc_read_calibration_data(gpadc);
|
||||
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
|
||||
dev_dbg(gpadc->dev, "probe success\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail_enable:
|
||||
fail_irq:
|
||||
free_irq(gpadc->irq, gpadc);
|
||||
free_irq(gpadc->irq_sw, gpadc);
|
||||
free_irq(gpadc->irq_hw, gpadc);
|
||||
fail:
|
||||
kfree(gpadc);
|
||||
gpadc = NULL;
|
||||
|
@ -687,7 +1014,10 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
|
|||
/* remove this gpadc entry from the list */
|
||||
list_del(&gpadc->node);
|
||||
/* remove interrupt - completion of Sw ADC conversion */
|
||||
free_irq(gpadc->irq, gpadc);
|
||||
if (gpadc->irq_sw >= 0)
|
||||
free_irq(gpadc->irq_sw, gpadc);
|
||||
if (gpadc->irq_hw >= 0)
|
||||
free_irq(gpadc->irq_hw, gpadc);
|
||||
|
||||
pm_runtime_get_sync(gpadc->dev);
|
||||
pm_runtime_disable(gpadc->dev);
|
||||
|
@ -707,6 +1037,9 @@ static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
|
|||
SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
|
||||
ab8500_gpadc_runtime_resume,
|
||||
ab8500_gpadc_runtime_idle)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend,
|
||||
ab8500_gpadc_resume)
|
||||
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_gpadc_driver = {
|
||||
|
@ -729,10 +1062,30 @@ static void __exit ab8500_gpadc_exit(void)
|
|||
platform_driver_unregister(&ab8500_gpadc_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8540_gpadc_get_otp() - returns OTP values
|
||||
*
|
||||
*/
|
||||
void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
|
||||
u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
|
||||
u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h)
|
||||
{
|
||||
*vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo;
|
||||
*vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi;
|
||||
*btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo;
|
||||
*btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi;
|
||||
*vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
|
||||
*vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
|
||||
*ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
|
||||
*ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
|
||||
return ;
|
||||
}
|
||||
|
||||
subsys_initcall_sync(ab8500_gpadc_init);
|
||||
module_exit(ab8500_gpadc_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
|
||||
MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
|
||||
"M'boumba Cedric Madianga");
|
||||
MODULE_ALIAS("platform:ab8500_gpadc");
|
||||
MODULE_DESCRIPTION("AB8500 GPADC driver");
|
||||
|
|
|
@ -15,19 +15,30 @@
|
|||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/abx500/ab8500-sysctrl.h>
|
||||
|
||||
/* RtcCtrl bits */
|
||||
#define AB8500_ALARM_MIN_LOW 0x08
|
||||
#define AB8500_ALARM_MIN_MID 0x09
|
||||
#define RTC_CTRL 0x0B
|
||||
#define RTC_ALARM_ENABLE 0x4
|
||||
|
||||
static struct device *sysctrl_dev;
|
||||
|
||||
void ab8500_power_off(void)
|
||||
{
|
||||
sigset_t old;
|
||||
sigset_t all;
|
||||
static char *pss[] = {"ab8500_ac", "ab8500_usb"};
|
||||
static char *pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
|
||||
int i;
|
||||
bool charger_present = false;
|
||||
union power_supply_propval val;
|
||||
struct power_supply *psy;
|
||||
int ret;
|
||||
|
||||
if (sysctrl_dev == NULL) {
|
||||
pr_err("%s: sysctrl not initialized\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a charger connected and we're powering off,
|
||||
* reboot into charge-only mode.
|
||||
|
@ -74,6 +85,63 @@ shutdown:
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the AB WD to reset the platform. It will perform a hard
|
||||
* reset instead of a soft reset. Write the reset reason to
|
||||
* the AB before reset, which can be read upon restart.
|
||||
*/
|
||||
void ab8500_restart(char mode, const char *cmd)
|
||||
{
|
||||
struct ab8500_platform_data *plat;
|
||||
struct ab8500_sysctrl_platform_data *pdata;
|
||||
u16 reason = 0;
|
||||
u8 val;
|
||||
|
||||
if (sysctrl_dev == NULL) {
|
||||
pr_err("%s: sysctrl not initialized\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
plat = dev_get_platdata(sysctrl_dev->parent);
|
||||
pdata = plat->sysctrl;
|
||||
if (pdata->reboot_reason_code)
|
||||
reason = pdata->reboot_reason_code(cmd);
|
||||
else
|
||||
pr_warn("[%s] No reboot reason set. Default reason %d\n",
|
||||
__func__, reason);
|
||||
|
||||
/*
|
||||
* Disable RTC alarm, just a precaution so that no alarm
|
||||
* is running when WD reset is executed.
|
||||
*/
|
||||
abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC,
|
||||
RTC_CTRL , &val);
|
||||
abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
|
||||
RTC_CTRL , (val & ~RTC_ALARM_ENABLE));
|
||||
|
||||
/*
|
||||
* Android is not using the RTC alarm registers during reboot
|
||||
* so we borrow them for writing the reason of reset
|
||||
*/
|
||||
|
||||
/* reason[8 LSB] */
|
||||
val = reason & 0xFF;
|
||||
abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
|
||||
AB8500_ALARM_MIN_LOW , val);
|
||||
|
||||
/* reason[8 MSB] */
|
||||
val = (reason>>8) & 0xFF;
|
||||
abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
|
||||
AB8500_ALARM_MIN_MID , val);
|
||||
|
||||
/* Setting WD timeout to 0 */
|
||||
ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0);
|
||||
|
||||
/* Setting the parameters to AB8500 WD*/
|
||||
ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD |
|
||||
AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD));
|
||||
}
|
||||
|
||||
static inline bool valid_bank(u8 bank)
|
||||
{
|
||||
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
|
||||
|
@ -85,7 +153,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
|
|||
u8 bank;
|
||||
|
||||
if (sysctrl_dev == NULL)
|
||||
return -EAGAIN;
|
||||
return -EINVAL;
|
||||
|
||||
bank = (reg >> 8);
|
||||
if (!valid_bank(bank))
|
||||
|
@ -101,7 +169,7 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
|
|||
u8 bank;
|
||||
|
||||
if (sysctrl_dev == NULL)
|
||||
return -EAGAIN;
|
||||
return -EINVAL;
|
||||
|
||||
bank = (reg >> 8);
|
||||
if (!valid_bank(bank))
|
||||
|
@ -114,28 +182,36 @@ EXPORT_SYMBOL(ab8500_sysctrl_write);
|
|||
|
||||
static int ab8500_sysctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct ab8500_platform_data *plat;
|
||||
struct ab8500_sysctrl_platform_data *pdata;
|
||||
|
||||
sysctrl_dev = &pdev->dev;
|
||||
plat = dev_get_platdata(pdev->dev.parent);
|
||||
|
||||
if (!(plat && plat->sysctrl))
|
||||
return -EINVAL;
|
||||
|
||||
if (plat->pm_power_off)
|
||||
pm_power_off = ab8500_power_off;
|
||||
|
||||
pdata = plat->sysctrl;
|
||||
|
||||
if (pdata) {
|
||||
int ret, i, j;
|
||||
int last, ret, i, j;
|
||||
|
||||
for (i = AB8500_SYSCLKREQ1RFCLKBUF;
|
||||
i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
|
||||
if (is_ab8505(ab8500))
|
||||
last = AB8500_SYSCLKREQ4RFCLKBUF;
|
||||
else
|
||||
last = AB8500_SYSCLKREQ8RFCLKBUF;
|
||||
|
||||
for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) {
|
||||
j = i - AB8500_SYSCLKREQ1RFCLKBUF;
|
||||
ret = ab8500_sysctrl_write(i, 0xff,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
pdata->initial_req_buf_config[j]);
|
||||
dev_dbg(&pdev->dev,
|
||||
"Setting SysClkReq%dRfClkBuf 0x%X\n",
|
||||
j + 1,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
"Setting SysClkReq%dRfClkBuf 0x%X\n",
|
||||
j + 1,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to set sysClkReq%dRfClkBuf: "
|
||||
|
|
|
@ -353,13 +353,6 @@ config BATTERY_GOLDFISH
|
|||
Say Y to enable support for the battery and AC power in the
|
||||
Goldfish emulator.
|
||||
|
||||
config CHARGER_PM2301
|
||||
bool "PM2301 Battery Charger Driver"
|
||||
depends on AB8500_BM
|
||||
help
|
||||
Say Y to include support for PM2301 charger driver.
|
||||
Depends on AB8500 battery management core.
|
||||
|
||||
source "drivers/power/reset/Kconfig"
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
|
|
@ -39,7 +39,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
|||
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
|
||||
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
|
||||
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
|
||||
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
|
||||
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
|
||||
|
@ -47,7 +47,6 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
|
|||
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
|
||||
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
|
||||
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
|
||||
obj-$(CONFIG_CHARGER_PM2301) += pm2301_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
|
||||
|
|
|
@ -407,15 +407,27 @@ static const struct abx500_fg_parameters fg = {
|
|||
.battok_raising_th_sel1 = 2860,
|
||||
.maint_thres = 95,
|
||||
.user_cap_limit = 15,
|
||||
.pcut_enable = 1,
|
||||
.pcut_max_time = 127,
|
||||
.pcut_flag_time = 112,
|
||||
.pcut_max_restart = 15,
|
||||
.pcut_debounce_time = 2,
|
||||
};
|
||||
|
||||
static const struct abx500_maxim_parameters maxi_params = {
|
||||
static const struct abx500_maxim_parameters ab8500_maxi_params = {
|
||||
.ena_maxi = true,
|
||||
.chg_curr = 910,
|
||||
.wait_cycles = 10,
|
||||
.charger_curr_step = 100,
|
||||
};
|
||||
|
||||
static const struct abx500_maxim_parameters abx540_maxi_params = {
|
||||
.ena_maxi = true,
|
||||
.chg_curr = 3000,
|
||||
.wait_cycles = 10,
|
||||
.charger_curr_step = 200,
|
||||
};
|
||||
|
||||
static const struct abx500_bm_charger_parameters chg = {
|
||||
.usb_volt_max = 5500,
|
||||
.usb_curr_max = 1500,
|
||||
|
@ -423,6 +435,46 @@ static const struct abx500_bm_charger_parameters chg = {
|
|||
.ac_curr_max = 1500,
|
||||
};
|
||||
|
||||
/*
|
||||
* This array maps the raw hex value to charger output current used by the
|
||||
* AB8500 values
|
||||
*/
|
||||
static int ab8500_charge_output_curr_map[] = {
|
||||
100, 200, 300, 400, 500, 600, 700, 800,
|
||||
900, 1000, 1100, 1200, 1300, 1400, 1500, 1500,
|
||||
};
|
||||
|
||||
static int ab8540_charge_output_curr_map[] = {
|
||||
0, 0, 0, 75, 100, 125, 150, 175,
|
||||
200, 225, 250, 275, 300, 325, 350, 375,
|
||||
400, 425, 450, 475, 500, 525, 550, 575,
|
||||
600, 625, 650, 675, 700, 725, 750, 775,
|
||||
800, 825, 850, 875, 900, 925, 950, 975,
|
||||
1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175,
|
||||
1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375,
|
||||
1400, 1425, 1450, 1500, 1600, 1700, 1900, 2000,
|
||||
};
|
||||
|
||||
/*
|
||||
* This array maps the raw hex value to charger input current used by the
|
||||
* AB8500 values
|
||||
*/
|
||||
static int ab8500_charge_input_curr_map[] = {
|
||||
50, 98, 193, 290, 380, 450, 500, 600,
|
||||
700, 800, 900, 1000, 1100, 1300, 1400, 1500,
|
||||
};
|
||||
|
||||
static int ab8540_charge_input_curr_map[] = {
|
||||
25, 50, 75, 100, 125, 150, 175, 200,
|
||||
225, 250, 275, 300, 325, 350, 375, 400,
|
||||
425, 450, 475, 500, 525, 550, 575, 600,
|
||||
625, 650, 675, 700, 725, 750, 775, 800,
|
||||
825, 850, 875, 900, 925, 950, 975, 1000,
|
||||
1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200,
|
||||
1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400,
|
||||
1425, 1450, 1475, 1500, 1500, 1500, 1500, 1500,
|
||||
};
|
||||
|
||||
struct abx500_bm_data ab8500_bm_data = {
|
||||
.temp_under = 3,
|
||||
.temp_low = 8,
|
||||
|
@ -442,15 +494,53 @@ struct abx500_bm_data ab8500_bm_data = {
|
|||
.fg_res = 100,
|
||||
.cap_levels = &cap_levels,
|
||||
.bat_type = bat_type_thermistor,
|
||||
.n_btypes = 3,
|
||||
.n_btypes = ARRAY_SIZE(bat_type_thermistor),
|
||||
.batt_id = 0,
|
||||
.interval_charging = 5,
|
||||
.interval_not_charging = 120,
|
||||
.temp_hysteresis = 3,
|
||||
.gnd_lift_resistance = 34,
|
||||
.maxi = &maxi_params,
|
||||
.chg_output_curr = ab8500_charge_output_curr_map,
|
||||
.n_chg_out_curr = ARRAY_SIZE(ab8500_charge_output_curr_map),
|
||||
.maxi = &ab8500_maxi_params,
|
||||
.chg_params = &chg,
|
||||
.fg_params = &fg,
|
||||
.chg_input_curr = ab8500_charge_input_curr_map,
|
||||
.n_chg_in_curr = ARRAY_SIZE(ab8500_charge_input_curr_map),
|
||||
};
|
||||
|
||||
struct abx500_bm_data ab8540_bm_data = {
|
||||
.temp_under = 3,
|
||||
.temp_low = 8,
|
||||
.temp_high = 43,
|
||||
.temp_over = 48,
|
||||
.main_safety_tmr_h = 4,
|
||||
.temp_interval_chg = 20,
|
||||
.temp_interval_nochg = 120,
|
||||
.usb_safety_tmr_h = 4,
|
||||
.bkup_bat_v = BUP_VCH_SEL_2P6V,
|
||||
.bkup_bat_i = BUP_ICH_SEL_150UA,
|
||||
.no_maintenance = false,
|
||||
.capacity_scaling = false,
|
||||
.adc_therm = ABx500_ADC_THERM_BATCTRL,
|
||||
.chg_unknown_bat = false,
|
||||
.enable_overshoot = false,
|
||||
.fg_res = 100,
|
||||
.cap_levels = &cap_levels,
|
||||
.bat_type = bat_type_thermistor,
|
||||
.n_btypes = ARRAY_SIZE(bat_type_thermistor),
|
||||
.batt_id = 0,
|
||||
.interval_charging = 5,
|
||||
.interval_not_charging = 120,
|
||||
.temp_hysteresis = 3,
|
||||
.gnd_lift_resistance = 0,
|
||||
.maxi = &abx540_maxi_params,
|
||||
.chg_params = &chg,
|
||||
.fg_params = &fg,
|
||||
.chg_output_curr = ab8540_charge_output_curr_map,
|
||||
.n_chg_out_curr = ARRAY_SIZE(ab8540_charge_output_curr_map),
|
||||
.chg_input_curr = ab8540_charge_input_curr_map,
|
||||
.n_chg_in_curr = ARRAY_SIZE(ab8540_charge_input_curr_map),
|
||||
};
|
||||
|
||||
int ab8500_bm_of_probe(struct device *dev,
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
#define BTEMP_BATCTRL_CURR_SRC_16UA 16
|
||||
#define BTEMP_BATCTRL_CURR_SRC_18UA 18
|
||||
|
||||
#define BTEMP_BATCTRL_CURR_SRC_60UA 60
|
||||
#define BTEMP_BATCTRL_CURR_SRC_120UA 120
|
||||
|
||||
#define to_ab8500_btemp_device_info(x) container_of((x), \
|
||||
struct ab8500_btemp, btemp_psy);
|
||||
|
||||
|
@ -76,8 +79,8 @@ struct ab8500_btemp_ranges {
|
|||
* @dev: Pointer to the structure device
|
||||
* @node: List of AB8500 BTEMPs, hence prepared for reentrance
|
||||
* @curr_source: What current source we use, in uA
|
||||
* @bat_temp: Battery temperature in degree Celcius
|
||||
* @prev_bat_temp Last dispatched battery temperature
|
||||
* @bat_temp: Dispatched battery temperature in degree Celcius
|
||||
* @prev_bat_temp Last measured battery temperature in degree Celcius
|
||||
* @parent: Pointer to the struct ab8500
|
||||
* @gpadc: Pointer to the struct gpadc
|
||||
* @fg: Pointer to the struct fg
|
||||
|
@ -155,7 +158,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
|
|||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
|
||||
/*
|
||||
* If the battery has internal NTC, we use the current
|
||||
* source to calculate the resistance, 7uA or 20uA
|
||||
* source to calculate the resistance.
|
||||
*/
|
||||
rbs = (v_batctrl * 1000
|
||||
- di->bm->gnd_lift_resistance * inst_curr)
|
||||
|
@ -216,7 +219,12 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
|||
/* Only do this for batteries with internal NTC */
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
|
||||
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
if (is_ab8540(di->parent)) {
|
||||
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_60UA)
|
||||
curr = BAT_CTRL_60U_ENA;
|
||||
else
|
||||
curr = BAT_CTRL_120U_ENA;
|
||||
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
|
||||
curr = BAT_CTRL_16U_ENA;
|
||||
else
|
||||
|
@ -257,7 +265,14 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
|||
} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
|
||||
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
|
||||
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
if (is_ab8540(di->parent)) {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(
|
||||
di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
|
||||
~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
|
||||
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(
|
||||
di->dev,
|
||||
|
@ -314,7 +329,13 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
|||
* if we got an error above
|
||||
*/
|
||||
disable_curr_source:
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
if (is_ab8540(di->parent)) {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
|
||||
~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
|
||||
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
|
@ -541,7 +562,9 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
|||
{
|
||||
int res;
|
||||
u8 i;
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent))
|
||||
if (is_ab8540(di->parent))
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
|
||||
else if (is_ab9540(di->parent) || is_ab8505(di->parent))
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
|
||||
else
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
|
||||
|
@ -579,12 +602,17 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
|||
|
||||
/*
|
||||
* We only have to change current source if the
|
||||
* detected type is Type 1, else we use the 7uA source
|
||||
* detected type is Type 1.
|
||||
*/
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
di->bm->batt_id == 1) {
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
dev_dbg(di->dev, "Set BATCTRL current source to 16uA\n");
|
||||
di->bm->batt_id == 1) {
|
||||
if (is_ab8540(di->parent)) {
|
||||
dev_dbg(di->dev,
|
||||
"Set BATCTRL current source to 60uA\n");
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
|
||||
} else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
dev_dbg(di->dev,
|
||||
"Set BATCTRL current source to 16uA\n");
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
|
||||
} else {
|
||||
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
|
||||
|
@ -604,22 +632,37 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
|||
static void ab8500_btemp_periodic_work(struct work_struct *work)
|
||||
{
|
||||
int interval;
|
||||
int bat_temp;
|
||||
struct ab8500_btemp *di = container_of(work,
|
||||
struct ab8500_btemp, btemp_periodic_work.work);
|
||||
|
||||
if (!di->initialized) {
|
||||
di->initialized = true;
|
||||
/* Identify the battery */
|
||||
if (ab8500_btemp_id(di) < 0)
|
||||
dev_warn(di->dev, "failed to identify the battery\n");
|
||||
}
|
||||
|
||||
di->bat_temp = ab8500_btemp_measure_temp(di);
|
||||
|
||||
if (di->bat_temp != di->prev_bat_temp) {
|
||||
di->prev_bat_temp = di->bat_temp;
|
||||
bat_temp = ab8500_btemp_measure_temp(di);
|
||||
/*
|
||||
* Filter battery temperature.
|
||||
* Allow direct updates on temperature only if two samples result in
|
||||
* same temperature. Else only allow 1 degree change from previous
|
||||
* reported value in the direction of the new measurement.
|
||||
*/
|
||||
if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
|
||||
if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
|
||||
di->initialized = true;
|
||||
di->bat_temp = bat_temp;
|
||||
power_supply_changed(&di->btemp_psy);
|
||||
}
|
||||
} else if (bat_temp < di->prev_bat_temp) {
|
||||
di->bat_temp--;
|
||||
power_supply_changed(&di->btemp_psy);
|
||||
} else if (bat_temp > di->prev_bat_temp) {
|
||||
di->bat_temp++;
|
||||
power_supply_changed(&di->btemp_psy);
|
||||
}
|
||||
di->prev_bat_temp = bat_temp;
|
||||
|
||||
if (di->events.ac_conn || di->events.usb_conn)
|
||||
interval = di->bm->temp_interval_chg;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -36,7 +36,7 @@
|
|||
|
||||
#define MILLI_TO_MICRO 1000
|
||||
#define FG_LSB_IN_MA 1627
|
||||
#define QLSB_NANO_AMP_HOURS_X10 1129
|
||||
#define QLSB_NANO_AMP_HOURS_X10 1071
|
||||
#define INS_CURR_TIMEOUT (3 * HZ)
|
||||
|
||||
#define SEC_TO_SAMPLE(S) (S * 4)
|
||||
|
@ -672,11 +672,11 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
|
|||
/*
|
||||
* Convert to unit value in mA
|
||||
* Full scale input voltage is
|
||||
* 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
|
||||
* 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA
|
||||
* Given a 250ms conversion cycle time the LSB corresponds
|
||||
* to 112.9 nAh. Convert to current by dividing by the conversion
|
||||
* to 107.1 nAh. Convert to current by dividing by the conversion
|
||||
* time in hours (250ms = 1 / (3600 * 4)h)
|
||||
* 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
|
||||
* 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm
|
||||
*/
|
||||
val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
|
||||
(1000 * di->bm->fg_res);
|
||||
|
@ -1354,9 +1354,6 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
|||
* algorithm says.
|
||||
*/
|
||||
di->bat_cap.prev_percent = 1;
|
||||
di->bat_cap.permille = 1;
|
||||
di->bat_cap.prev_mah = 1;
|
||||
di->bat_cap.mah = 1;
|
||||
percent = 1;
|
||||
|
||||
changed = true;
|
||||
|
@ -1683,7 +1680,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
|||
break;
|
||||
|
||||
case AB8500_FG_DISCHARGE_WAKEUP:
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_calc_cap_discharge_voltage(di, true);
|
||||
|
||||
di->fg_samples = SEC_TO_SAMPLE(
|
||||
|
@ -1768,9 +1764,10 @@ static void ab8500_fg_algorithm(struct ab8500_fg *di)
|
|||
ab8500_fg_algorithm_discharging(di);
|
||||
}
|
||||
|
||||
dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
|
||||
dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d "
|
||||
"%d %d %d %d %d %d %d\n",
|
||||
di->bat_cap.max_mah_design,
|
||||
di->bat_cap.max_mah,
|
||||
di->bat_cap.mah,
|
||||
di->bat_cap.permille,
|
||||
di->bat_cap.level,
|
||||
|
@ -1982,7 +1979,7 @@ static void ab8500_fg_instant_work(struct work_struct *work)
|
|||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_cc_data_end_handler() - isr to get battery avg current.
|
||||
* ab8500_fg_cc_data_end_handler() - end of data conversion isr.
|
||||
* @irq: interrupt number
|
||||
* @_di: pointer to the ab8500_fg structure
|
||||
*
|
||||
|
@ -2002,7 +1999,7 @@ static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
|
|||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_cc_convend_handler() - isr to get battery avg current.
|
||||
* ab8500_fg_cc_int_calib_handler () - end of calibration isr.
|
||||
* @irq: interrupt number
|
||||
* @_di: pointer to the ab8500_fg structure
|
||||
*
|
||||
|
@ -2153,9 +2150,7 @@ static int ab8500_fg_get_property(struct power_supply *psy,
|
|||
val->intval = di->bat_cap.prev_mah;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
if (di->bm->capacity_scaling)
|
||||
val->intval = di->bat_cap.cap_scale.scaled_cap;
|
||||
else if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
|
||||
if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
|
||||
di->flags.batt_id_received)
|
||||
val->intval = 100;
|
||||
else
|
||||
|
@ -2344,6 +2339,50 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
|
|||
dev_err(di->dev, "BattOk init write failed.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||
abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
|
||||
|| is_ab8540(di->parent)) {
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time);
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time);
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable);
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -2546,6 +2585,428 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
long unsigned reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
reg_value = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
if (reg_value > 0x7F) {
|
||||
dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
|
||||
|
||||
fail:
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_MAX_TIME_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
reg_value = simple_strtoul(buf, NULL, 10);
|
||||
if (reg_value > 0x7F) {
|
||||
dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
|
||||
|
||||
fail:
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_restart_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_RESTART_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_restart_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
reg_value = simple_strtoul(buf, NULL, 10);
|
||||
if (reg_value > 0xF) {
|
||||
dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n");
|
||||
|
||||
fail:
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_timer_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_TIME_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_RESTART_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
reg_value = simple_strtoul(buf, NULL, 10);
|
||||
if (reg_value > 0x1) {
|
||||
dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||
|
||||
fail:
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_flag_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_debounce_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_debounce_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
reg_value = simple_strtoul(buf, NULL, 10);
|
||||
if (reg_value > 0x7) {
|
||||
dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n");
|
||||
|
||||
fail:
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
|
||||
__ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
|
||||
__ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
|
||||
__ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_restart_read, ab8505_powercut_restart_write),
|
||||
__ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
|
||||
__ATTR(powercut_restart_counter, S_IRUGO,
|
||||
ab8505_powercut_restart_counter_read, NULL),
|
||||
__ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_read, ab8505_powercut_write),
|
||||
__ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
|
||||
__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
|
||||
__ATTR(powercut_enable_status, S_IRUGO,
|
||||
ab8505_powercut_enable_status_read, NULL),
|
||||
};
|
||||
|
||||
static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|
||||
|| is_ab8540(di->parent)) {
|
||||
for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++)
|
||||
if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j]))
|
||||
goto sysfs_psy_create_attrs_failed_ab8505;
|
||||
}
|
||||
return 0;
|
||||
sysfs_psy_create_attrs_failed_ab8505:
|
||||
dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n");
|
||||
while (j--)
|
||||
device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev)
|
||||
{
|
||||
unsigned int i;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
di = to_ab8500_fg_device_info(psy);
|
||||
|
||||
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|
||||
|| is_ab8540(di->parent)) {
|
||||
for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
|
||||
(void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Exposure to the sysfs interface <<END>> */
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
|
@ -2607,6 +3068,7 @@ static int ab8500_fg_remove(struct platform_device *pdev)
|
|||
ab8500_fg_sysfs_exit(di);
|
||||
|
||||
flush_scheduled_work();
|
||||
ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev);
|
||||
power_supply_unregister(&di->fg_psy);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return ret;
|
||||
|
@ -2772,6 +3234,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
|||
goto free_irq;
|
||||
}
|
||||
|
||||
ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev);
|
||||
if (ret) {
|
||||
dev_err(di->dev, "failed to create FG psy\n");
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
goto free_irq;
|
||||
}
|
||||
|
||||
/* Calibrate the fg first time */
|
||||
di->flags.calibrate = true;
|
||||
di->calib_state = AB8500_FG_CALIB_INIT;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2012
|
||||
* Copyright (c) 2012 Sony Mobile Communications AB
|
||||
*
|
||||
* Charging algorithm driver for abx500 variants
|
||||
*
|
||||
|
@ -8,11 +9,13 @@
|
|||
* Johan Palsson <johan.palsson@stericsson.com>
|
||||
* Karl Komierowski <karl.komierowski@stericsson.com>
|
||||
* Arun R Murthy <arun.murthy@stericsson.com>
|
||||
* Author: Imre Sunyi <imre.sunyi@sonymobile.com>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -24,8 +27,10 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/abx500/ux500_chargalg.h>
|
||||
#include <linux/mfd/abx500/ab8500-bm.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
/* Watchdog kick interval */
|
||||
#define CHG_WD_INTERVAL (6 * HZ)
|
||||
|
@ -33,6 +38,18 @@
|
|||
/* End-of-charge criteria counter */
|
||||
#define EOC_COND_CNT 10
|
||||
|
||||
/* One hour expressed in seconds */
|
||||
#define ONE_HOUR_IN_SECONDS 3600
|
||||
|
||||
/* Five minutes expressed in seconds */
|
||||
#define FIVE_MINUTES_IN_SECONDS 300
|
||||
|
||||
/* Plus margin for the low battery threshold */
|
||||
#define BAT_PLUS_MARGIN (100)
|
||||
|
||||
#define CHARGALG_CURR_STEP_LOW 0
|
||||
#define CHARGALG_CURR_STEP_HIGH 100
|
||||
|
||||
#define to_abx500_chargalg_device_info(x) container_of((x), \
|
||||
struct abx500_chargalg, chargalg_psy);
|
||||
|
||||
|
@ -66,6 +83,11 @@ struct abx500_chargalg_suspension_status {
|
|||
bool usb_suspended;
|
||||
};
|
||||
|
||||
struct abx500_chargalg_current_step_status {
|
||||
bool curr_step_change;
|
||||
int curr_step;
|
||||
};
|
||||
|
||||
struct abx500_chargalg_battery_data {
|
||||
int temp;
|
||||
int volt;
|
||||
|
@ -82,6 +104,7 @@ enum abx500_chargalg_states {
|
|||
STATE_HW_TEMP_PROTECT_INIT,
|
||||
STATE_HW_TEMP_PROTECT,
|
||||
STATE_NORMAL_INIT,
|
||||
STATE_USB_PP_PRE_CHARGE,
|
||||
STATE_NORMAL,
|
||||
STATE_WAIT_FOR_RECHARGE_INIT,
|
||||
STATE_WAIT_FOR_RECHARGE,
|
||||
|
@ -113,6 +136,7 @@ static const char *states[] = {
|
|||
"HW_TEMP_PROTECT_INIT",
|
||||
"HW_TEMP_PROTECT",
|
||||
"NORMAL_INIT",
|
||||
"USB_PP_PRE_CHARGE",
|
||||
"NORMAL",
|
||||
"WAIT_FOR_RECHARGE_INIT",
|
||||
"WAIT_FOR_RECHARGE",
|
||||
|
@ -204,6 +228,8 @@ enum maxim_ret {
|
|||
* @batt_data: data of the battery
|
||||
* @susp_status: current charger suspension status
|
||||
* @bm: Platform specific battery management information
|
||||
* @curr_status: Current step status for over-current protection
|
||||
* @parent: pointer to the struct abx500
|
||||
* @chargalg_psy: structure that holds the battery properties exposed by
|
||||
* the charging algorithm
|
||||
* @events: structure for information about events triggered
|
||||
|
@ -227,6 +253,8 @@ struct abx500_chargalg {
|
|||
struct abx500_chargalg_charger_info chg_info;
|
||||
struct abx500_chargalg_battery_data batt_data;
|
||||
struct abx500_chargalg_suspension_status susp_status;
|
||||
struct ab8500 *parent;
|
||||
struct abx500_chargalg_current_step_status curr_status;
|
||||
struct abx500_bm_data *bm;
|
||||
struct power_supply chargalg_psy;
|
||||
struct ux500_charger *ac_chg;
|
||||
|
@ -236,51 +264,69 @@ struct abx500_chargalg {
|
|||
struct delayed_work chargalg_periodic_work;
|
||||
struct delayed_work chargalg_wd_work;
|
||||
struct work_struct chargalg_work;
|
||||
struct timer_list safety_timer;
|
||||
struct timer_list maintenance_timer;
|
||||
struct hrtimer safety_timer;
|
||||
struct hrtimer maintenance_timer;
|
||||
struct kobject chargalg_kobject;
|
||||
};
|
||||
|
||||
/*External charger prepare notifier*/
|
||||
BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
|
||||
|
||||
/* Main battery properties */
|
||||
static enum power_supply_property abx500_chargalg_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
};
|
||||
|
||||
struct abx500_chargalg_sysfs_entry {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct abx500_chargalg *, char *);
|
||||
ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
|
||||
};
|
||||
|
||||
/**
|
||||
* abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
|
||||
* @data: pointer to the abx500_chargalg structure
|
||||
* @timer: pointer to the hrtimer structure
|
||||
*
|
||||
* This function gets called when the safety timer for the charger
|
||||
* expires
|
||||
*/
|
||||
static void abx500_chargalg_safety_timer_expired(unsigned long data)
|
||||
static enum hrtimer_restart
|
||||
abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
|
||||
{
|
||||
struct abx500_chargalg *di = (struct abx500_chargalg *) data;
|
||||
struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
|
||||
safety_timer);
|
||||
dev_err(di->dev, "Safety timer expired\n");
|
||||
di->events.safety_timer_expired = true;
|
||||
|
||||
/* Trigger execution of the algorithm instantly */
|
||||
queue_work(di->chargalg_wq, &di->chargalg_work);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_maintenance_timer_expired() - Expiration of
|
||||
* the maintenance timer
|
||||
* @i: pointer to the abx500_chargalg structure
|
||||
* @timer: pointer to the timer structure
|
||||
*
|
||||
* This function gets called when the maintenence timer
|
||||
* expires
|
||||
*/
|
||||
static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
|
||||
static enum hrtimer_restart
|
||||
abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
|
||||
{
|
||||
|
||||
struct abx500_chargalg *di = (struct abx500_chargalg *) data;
|
||||
struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
|
||||
maintenance_timer);
|
||||
|
||||
dev_dbg(di->dev, "Maintenance timer expired\n");
|
||||
di->events.maintenance_timer_expired = true;
|
||||
|
||||
/* Trigger execution of the algorithm instantly */
|
||||
queue_work(di->chargalg_wq, &di->chargalg_work);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,6 +349,30 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di,
|
|||
di->charge_state = state;
|
||||
}
|
||||
|
||||
static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
|
||||
{
|
||||
switch (di->charge_state) {
|
||||
case STATE_NORMAL:
|
||||
case STATE_MAINTENANCE_A:
|
||||
case STATE_MAINTENANCE_B:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (di->chg_info.charger_type & USB_CHG) {
|
||||
return di->usb_chg->ops.check_enable(di->usb_chg,
|
||||
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
|
||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
|
||||
} else if ((di->chg_info.charger_type & AC_CHG) &&
|
||||
!(di->ac_chg->external)) {
|
||||
return di->ac_chg->ops.check_enable(di->ac_chg,
|
||||
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
|
||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_check_charger_connection() - Check charger connection change
|
||||
* @di: pointer to the abx500_chargalg structure
|
||||
|
@ -347,6 +417,22 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
|
|||
return di->chg_info.conn_chg;
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_check_current_step_status() - Check charging current
|
||||
* step status.
|
||||
* @di: pointer to the abx500_chargalg structure
|
||||
*
|
||||
* This function will check if there is a change in the charging current step
|
||||
* and change charge state accordingly.
|
||||
*/
|
||||
static void abx500_chargalg_check_current_step_status
|
||||
(struct abx500_chargalg *di)
|
||||
{
|
||||
if (di->curr_status.curr_step_change)
|
||||
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
|
||||
di->curr_status.curr_step_change = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_start_safety_timer() - Start charging safety timer
|
||||
* @di: pointer to the abx500_chargalg structure
|
||||
|
@ -356,19 +442,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
|
|||
*/
|
||||
static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
|
||||
{
|
||||
unsigned long timer_expiration = 0;
|
||||
/* Charger-dependent expiration time in hours*/
|
||||
int timer_expiration = 0;
|
||||
|
||||
switch (di->chg_info.charger_type) {
|
||||
case AC_CHG:
|
||||
timer_expiration =
|
||||
round_jiffies(jiffies +
|
||||
(di->bm->main_safety_tmr_h * 3600 * HZ));
|
||||
timer_expiration = di->bm->main_safety_tmr_h;
|
||||
break;
|
||||
|
||||
case USB_CHG:
|
||||
timer_expiration =
|
||||
round_jiffies(jiffies +
|
||||
(di->bm->usb_safety_tmr_h * 3600 * HZ));
|
||||
timer_expiration = di->bm->usb_safety_tmr_h;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -377,11 +460,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
|
|||
}
|
||||
|
||||
di->events.safety_timer_expired = false;
|
||||
di->safety_timer.expires = timer_expiration;
|
||||
if (!timer_pending(&di->safety_timer))
|
||||
add_timer(&di->safety_timer);
|
||||
else
|
||||
mod_timer(&di->safety_timer, timer_expiration);
|
||||
hrtimer_set_expires_range(&di->safety_timer,
|
||||
ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
|
||||
ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
|
||||
hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -392,8 +474,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
|
|||
*/
|
||||
static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
|
||||
{
|
||||
di->events.safety_timer_expired = false;
|
||||
del_timer(&di->safety_timer);
|
||||
if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
|
||||
di->events.safety_timer_expired = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -408,17 +490,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
|
|||
static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
|
||||
int duration)
|
||||
{
|
||||
unsigned long timer_expiration;
|
||||
|
||||
/* Convert from hours to jiffies */
|
||||
timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
|
||||
|
||||
hrtimer_set_expires_range(&di->maintenance_timer,
|
||||
ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
|
||||
ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
|
||||
di->events.maintenance_timer_expired = false;
|
||||
di->maintenance_timer.expires = timer_expiration;
|
||||
if (!timer_pending(&di->maintenance_timer))
|
||||
add_timer(&di->maintenance_timer);
|
||||
else
|
||||
mod_timer(&di->maintenance_timer, timer_expiration);
|
||||
hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -430,8 +506,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
|
|||
*/
|
||||
static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
|
||||
{
|
||||
di->events.maintenance_timer_expired = false;
|
||||
del_timer(&di->maintenance_timer);
|
||||
if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
|
||||
di->events.maintenance_timer_expired = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -477,6 +553,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
|
|||
static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
|
||||
int vset, int iset)
|
||||
{
|
||||
static int abx500_chargalg_ex_ac_enable_toggle;
|
||||
|
||||
if (!di->ac_chg || !di->ac_chg->ops.enable)
|
||||
return -ENXIO;
|
||||
|
||||
|
@ -489,6 +567,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
|
|||
di->chg_info.ac_iset = iset;
|
||||
di->chg_info.ac_vset = vset;
|
||||
|
||||
/* Enable external charger */
|
||||
if (enable && di->ac_chg->external &&
|
||||
!abx500_chargalg_ex_ac_enable_toggle) {
|
||||
blocking_notifier_call_chain(&charger_notifier_list,
|
||||
0, di->dev);
|
||||
abx500_chargalg_ex_ac_enable_toggle++;
|
||||
}
|
||||
|
||||
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
|
||||
}
|
||||
|
||||
|
@ -520,6 +606,37 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
|
|||
return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path
|
||||
* @di: pointer to the abx500_chargalg structure
|
||||
* @enable: power path enable/disable
|
||||
*
|
||||
* The USB power path will be enable/ disable
|
||||
*/
|
||||
static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable)
|
||||
{
|
||||
if (!di->usb_chg || !di->usb_chg->ops.pp_enable)
|
||||
return -ENXIO;
|
||||
|
||||
return di->usb_chg->ops.pp_enable(di->usb_chg, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge
|
||||
* @di: pointer to the abx500_chargalg structure
|
||||
* @enable: USB pre-charge enable/disable
|
||||
*
|
||||
* The USB USB pre-charge will be enable/ disable
|
||||
*/
|
||||
static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di,
|
||||
bool enable)
|
||||
{
|
||||
if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable)
|
||||
return -ENXIO;
|
||||
|
||||
return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_update_chg_curr() - Update charger current
|
||||
* @di: pointer to the abx500_chargalg structure
|
||||
|
@ -613,8 +730,6 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
|
|||
static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
|
||||
int vset, int iset)
|
||||
{
|
||||
bool start_chargalg_wd = true;
|
||||
|
||||
switch (di->chg_info.charger_type) {
|
||||
case AC_CHG:
|
||||
dev_dbg(di->dev,
|
||||
|
@ -632,12 +747,8 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
|
|||
|
||||
default:
|
||||
dev_err(di->dev, "Unknown charger to charge from\n");
|
||||
start_chargalg_wd = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (start_chargalg_wd && !delayed_work_pending(&di->chargalg_wd_work))
|
||||
queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -725,6 +836,9 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
|
|||
di->batt_data.avg_curr > 0) {
|
||||
if (++di->eoc_cnt >= EOC_COND_CNT) {
|
||||
di->eoc_cnt = 0;
|
||||
if ((di->chg_info.charger_type & USB_CHG) &&
|
||||
(di->usb_chg->power_path))
|
||||
ab8540_chargalg_usb_pp_en(di, true);
|
||||
di->charge_status = POWER_SUPPLY_STATUS_FULL;
|
||||
di->maintenance_chg = true;
|
||||
dev_dbg(di->dev, "EOC reached!\n");
|
||||
|
@ -1217,6 +1331,8 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy)
|
|||
static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
{
|
||||
int charger_status;
|
||||
int ret;
|
||||
int curr_step_lvl;
|
||||
|
||||
/* Collect data from all power_supply class devices */
|
||||
class_for_each_device(power_supply_class, NULL,
|
||||
|
@ -1227,6 +1343,15 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
|||
abx500_chargalg_check_charger_voltage(di);
|
||||
|
||||
charger_status = abx500_chargalg_check_charger_connection(di);
|
||||
abx500_chargalg_check_current_step_status(di);
|
||||
|
||||
if (is_ab8500(di->parent)) {
|
||||
ret = abx500_chargalg_check_charger_enable(di);
|
||||
if (ret < 0)
|
||||
dev_err(di->dev, "Checking charger is enabled error"
|
||||
": Returned Value %d\n", ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* First check if we have a charger connected.
|
||||
* Also we don't allow charging of unknown batteries if configured
|
||||
|
@ -1416,9 +1541,34 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
|||
break;
|
||||
|
||||
case STATE_NORMAL_INIT:
|
||||
abx500_chargalg_start_charging(di,
|
||||
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
|
||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
|
||||
if ((di->chg_info.charger_type & USB_CHG) &&
|
||||
di->usb_chg->power_path) {
|
||||
if (di->batt_data.volt >
|
||||
(di->bm->fg_params->lowbat_threshold +
|
||||
BAT_PLUS_MARGIN)) {
|
||||
ab8540_chargalg_usb_pre_chg_en(di, false);
|
||||
ab8540_chargalg_usb_pp_en(di, false);
|
||||
} else {
|
||||
ab8540_chargalg_usb_pp_en(di, true);
|
||||
ab8540_chargalg_usb_pre_chg_en(di, true);
|
||||
abx500_chargalg_state_to(di,
|
||||
STATE_USB_PP_PRE_CHARGE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
|
||||
abx500_chargalg_stop_charging(di);
|
||||
else {
|
||||
curr_step_lvl = di->bm->bat_type[
|
||||
di->bm->batt_id].normal_cur_lvl
|
||||
* di->curr_status.curr_step
|
||||
/ CHARGALG_CURR_STEP_HIGH;
|
||||
abx500_chargalg_start_charging(di,
|
||||
di->bm->bat_type[di->bm->batt_id]
|
||||
.normal_vol_lvl, curr_step_lvl);
|
||||
}
|
||||
|
||||
abx500_chargalg_state_to(di, STATE_NORMAL);
|
||||
abx500_chargalg_start_safety_timer(di);
|
||||
abx500_chargalg_stop_maintenance_timer(di);
|
||||
|
@ -1430,6 +1580,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
|||
|
||||
break;
|
||||
|
||||
case STATE_USB_PP_PRE_CHARGE:
|
||||
if (di->batt_data.volt >
|
||||
(di->bm->fg_params->lowbat_threshold +
|
||||
BAT_PLUS_MARGIN))
|
||||
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
|
||||
break;
|
||||
|
||||
case STATE_NORMAL:
|
||||
handle_maxim_chg_curr(di);
|
||||
if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
|
||||
|
@ -1653,99 +1810,134 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
|
|||
|
||||
/* Exposure to the sysfs interface */
|
||||
|
||||
/**
|
||||
* abx500_chargalg_sysfs_show() - sysfs show operations
|
||||
* @kobj: pointer to the struct kobject
|
||||
* @attr: pointer to the struct attribute
|
||||
* @buf: buffer that holds the parameter to send to userspace
|
||||
*
|
||||
* Returns a buffer to be displayed in user space
|
||||
*/
|
||||
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
|
||||
char *buf)
|
||||
{
|
||||
struct abx500_chargalg *di = container_of(kobj,
|
||||
struct abx500_chargalg, chargalg_kobject);
|
||||
return sprintf(buf, "%d\n", di->curr_status.curr_step);
|
||||
}
|
||||
|
||||
static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
|
||||
const char *buf, size_t length)
|
||||
{
|
||||
long int param;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, ¶m);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
di->curr_status.curr_step = param;
|
||||
if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW &&
|
||||
di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) {
|
||||
di->curr_status.curr_step_change = true;
|
||||
queue_work(di->chargalg_wq, &di->chargalg_work);
|
||||
} else
|
||||
dev_info(di->dev, "Wrong current step\n"
|
||||
"Enter 0. Disable AC/USB Charging\n"
|
||||
"1--100. Set AC/USB charging current step\n"
|
||||
"100. Enable AC/USB Charging\n");
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n",
|
||||
di->susp_status.ac_suspended &&
|
||||
di->susp_status.usb_suspended);
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_sysfs_charger() - sysfs store operations
|
||||
* @kobj: pointer to the struct kobject
|
||||
* @attr: pointer to the struct attribute
|
||||
* @buf: buffer that holds the parameter passed from userspace
|
||||
* @length: length of the parameter passed
|
||||
*
|
||||
* Returns length of the buffer(input taken from user space) on success
|
||||
* else error code on failure
|
||||
* The operation to be performed on passing the parameters from the user space.
|
||||
*/
|
||||
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
|
||||
struct attribute *attr, const char *buf, size_t length)
|
||||
static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
|
||||
const char *buf, size_t length)
|
||||
{
|
||||
struct abx500_chargalg *di = container_of(kobj,
|
||||
struct abx500_chargalg, chargalg_kobject);
|
||||
long int param;
|
||||
int ac_usb;
|
||||
int ret;
|
||||
char entry = *attr->name;
|
||||
|
||||
switch (entry) {
|
||||
case 'c':
|
||||
ret = strict_strtol(buf, 10, ¶m);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = kstrtol(buf, 10, ¶m);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ac_usb = param;
|
||||
switch (ac_usb) {
|
||||
case 0:
|
||||
/* Disable charging */
|
||||
di->susp_status.ac_suspended = true;
|
||||
di->susp_status.usb_suspended = true;
|
||||
di->susp_status.suspended_change = true;
|
||||
/* Trigger a state change */
|
||||
queue_work(di->chargalg_wq,
|
||||
&di->chargalg_work);
|
||||
break;
|
||||
case 1:
|
||||
/* Enable AC Charging */
|
||||
di->susp_status.ac_suspended = false;
|
||||
di->susp_status.suspended_change = true;
|
||||
/* Trigger a state change */
|
||||
queue_work(di->chargalg_wq,
|
||||
&di->chargalg_work);
|
||||
break;
|
||||
case 2:
|
||||
/* Enable USB charging */
|
||||
di->susp_status.usb_suspended = false;
|
||||
di->susp_status.suspended_change = true;
|
||||
/* Trigger a state change */
|
||||
queue_work(di->chargalg_wq,
|
||||
&di->chargalg_work);
|
||||
break;
|
||||
default:
|
||||
dev_info(di->dev, "Wrong input\n"
|
||||
"Enter 0. Disable AC/USB Charging\n"
|
||||
"1. Enable AC charging\n"
|
||||
"2. Enable USB Charging\n");
|
||||
};
|
||||
ac_usb = param;
|
||||
switch (ac_usb) {
|
||||
case 0:
|
||||
/* Disable charging */
|
||||
di->susp_status.ac_suspended = true;
|
||||
di->susp_status.usb_suspended = true;
|
||||
di->susp_status.suspended_change = true;
|
||||
/* Trigger a state change */
|
||||
queue_work(di->chargalg_wq,
|
||||
&di->chargalg_work);
|
||||
break;
|
||||
case 1:
|
||||
/* Enable AC Charging */
|
||||
di->susp_status.ac_suspended = false;
|
||||
di->susp_status.suspended_change = true;
|
||||
/* Trigger a state change */
|
||||
queue_work(di->chargalg_wq,
|
||||
&di->chargalg_work);
|
||||
break;
|
||||
case 2:
|
||||
/* Enable USB charging */
|
||||
di->susp_status.usb_suspended = false;
|
||||
di->susp_status.suspended_change = true;
|
||||
/* Trigger a state change */
|
||||
queue_work(di->chargalg_wq,
|
||||
&di->chargalg_work);
|
||||
break;
|
||||
default:
|
||||
dev_info(di->dev, "Wrong input\n"
|
||||
"Enter 0. Disable AC/USB Charging\n"
|
||||
"1. Enable AC charging\n"
|
||||
"2. Enable USB Charging\n");
|
||||
};
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static struct attribute abx500_chargalg_en_charger = \
|
||||
static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
|
||||
__ATTR(chargalg, 0644, abx500_chargalg_en_show,
|
||||
abx500_chargalg_en_store);
|
||||
|
||||
static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
|
||||
__ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
|
||||
abx500_chargalg_curr_step_store);
|
||||
|
||||
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
.name = "chargalg",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
};
|
||||
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
|
||||
struct abx500_chargalg_sysfs_entry, attr);
|
||||
|
||||
struct abx500_chargalg *di = container_of(kobj,
|
||||
struct abx500_chargalg, chargalg_kobject);
|
||||
|
||||
if (!entry->show)
|
||||
return -EIO;
|
||||
|
||||
return entry->show(di, buf);
|
||||
}
|
||||
|
||||
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
|
||||
struct attribute *attr, const char *buf, size_t length)
|
||||
{
|
||||
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
|
||||
struct abx500_chargalg_sysfs_entry, attr);
|
||||
|
||||
struct abx500_chargalg *di = container_of(kobj,
|
||||
struct abx500_chargalg, chargalg_kobject);
|
||||
|
||||
if (!entry->store)
|
||||
return -EIO;
|
||||
|
||||
return entry->store(di, buf, length);
|
||||
}
|
||||
|
||||
static struct attribute *abx500_chargalg_chg[] = {
|
||||
&abx500_chargalg_en_charger,
|
||||
NULL
|
||||
&abx500_chargalg_en_charger.attr,
|
||||
&abx500_chargalg_curr_step.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
|
||||
|
@ -1832,10 +2024,16 @@ static int abx500_chargalg_remove(struct platform_device *pdev)
|
|||
/* sysfs interface to enable/disbale charging from user space */
|
||||
abx500_chargalg_sysfs_exit(di);
|
||||
|
||||
hrtimer_cancel(&di->safety_timer);
|
||||
hrtimer_cancel(&di->maintenance_timer);
|
||||
|
||||
cancel_delayed_work_sync(&di->chargalg_periodic_work);
|
||||
cancel_delayed_work_sync(&di->chargalg_wd_work);
|
||||
cancel_work_sync(&di->chargalg_work);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->chargalg_wq);
|
||||
|
||||
flush_scheduled_work();
|
||||
power_supply_unregister(&di->chargalg_psy);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
|
@ -1873,8 +2071,9 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
/* get device struct */
|
||||
/* get device struct and parent */
|
||||
di->dev = &pdev->dev;
|
||||
di->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
/* chargalg supply */
|
||||
di->chargalg_psy.name = "abx500_chargalg";
|
||||
|
@ -1888,15 +2087,13 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
|||
abx500_chargalg_external_power_changed;
|
||||
|
||||
/* Initilialize safety timer */
|
||||
init_timer(&di->safety_timer);
|
||||
hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
|
||||
di->safety_timer.function = abx500_chargalg_safety_timer_expired;
|
||||
di->safety_timer.data = (unsigned long) di;
|
||||
|
||||
/* Initilialize maintenance timer */
|
||||
init_timer(&di->maintenance_timer);
|
||||
hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
|
||||
di->maintenance_timer.function =
|
||||
abx500_chargalg_maintenance_timer_expired;
|
||||
di->maintenance_timer.data = (unsigned long) di;
|
||||
|
||||
/* Create a work queue for the chargalg */
|
||||
di->chargalg_wq =
|
||||
|
@ -1933,6 +2130,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
|||
dev_err(di->dev, "failed to create sysfs entry\n");
|
||||
goto free_psy;
|
||||
}
|
||||
di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
|
||||
|
||||
/* Run the charging algorithm */
|
||||
queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
|
||||
|
@ -1964,18 +2162,7 @@ static struct platform_driver abx500_chargalg_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init abx500_chargalg_init(void)
|
||||
{
|
||||
return platform_driver_register(&abx500_chargalg_driver);
|
||||
}
|
||||
|
||||
static void __exit abx500_chargalg_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&abx500_chargalg_driver);
|
||||
}
|
||||
|
||||
module_init(abx500_chargalg_init);
|
||||
module_exit(abx500_chargalg_exit);
|
||||
module_platform_driver(abx500_chargalg_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
|
||||
|
|
|
@ -16,24 +16,24 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/abx500/ab8500-bm.h>
|
||||
#include <linux/mfd/abx500/ab8500-gpadc.h>
|
||||
#include <linux/mfd/abx500/ux500_chargalg.h>
|
||||
#include <linux/pm2301_charger.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "pm2301_charger.h"
|
||||
|
||||
#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
|
||||
struct pm2xxx_charger, ac_chg)
|
||||
#define SLEEP_MIN 50
|
||||
#define SLEEP_MAX 100
|
||||
#define PM2XXX_AUTOSUSPEND_DELAY 500
|
||||
|
||||
static int pm2xxx_interrupt_registers[] = {
|
||||
PM2XXX_REG_INT1,
|
||||
|
@ -113,33 +113,24 @@ static const struct i2c_device_id pm2xxx_ident[] = {
|
|||
|
||||
static void set_lpn_pin(struct pm2xxx_charger *pm2)
|
||||
{
|
||||
if (pm2->ac.charger_connected)
|
||||
return;
|
||||
gpio_set_value(pm2->lpn_pin, 1);
|
||||
|
||||
return;
|
||||
if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) {
|
||||
gpio_set_value(pm2->lpn_pin, 1);
|
||||
usleep_range(SLEEP_MIN, SLEEP_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_lpn_pin(struct pm2xxx_charger *pm2)
|
||||
{
|
||||
if (pm2->ac.charger_connected)
|
||||
return;
|
||||
gpio_set_value(pm2->lpn_pin, 0);
|
||||
|
||||
return;
|
||||
if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin))
|
||||
gpio_set_value(pm2->lpn_pin, 0);
|
||||
}
|
||||
|
||||
static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
|
||||
{
|
||||
int ret;
|
||||
/*
|
||||
* When AC adaptor is unplugged, the host
|
||||
* must put LPN high to be able to
|
||||
* communicate by I2C with PM2301
|
||||
* and receive I2C "acknowledge" from PM2301.
|
||||
*/
|
||||
mutex_lock(&pm2->lock);
|
||||
set_lpn_pin(pm2);
|
||||
|
||||
/* wake up the device */
|
||||
pm_runtime_get_sync(pm2->dev);
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
|
||||
1, val);
|
||||
|
@ -147,8 +138,8 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
|
|||
dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
|
||||
else
|
||||
ret = 0;
|
||||
clear_lpn_pin(pm2);
|
||||
mutex_unlock(&pm2->lock);
|
||||
|
||||
pm_runtime_put_sync(pm2->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -156,14 +147,9 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
|
|||
static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
/*
|
||||
* When AC adaptor is unplugged, the host
|
||||
* must put LPN high to be able to
|
||||
* communicate by I2C with PM2301
|
||||
* and receive I2C "acknowledge" from PM2301.
|
||||
*/
|
||||
mutex_lock(&pm2->lock);
|
||||
set_lpn_pin(pm2);
|
||||
|
||||
/* wake up the device */
|
||||
pm_runtime_get_sync(pm2->dev);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
|
||||
1, &val);
|
||||
|
@ -171,8 +157,8 @@ static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
|
|||
dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
|
||||
else
|
||||
ret = 0;
|
||||
clear_lpn_pin(pm2);
|
||||
mutex_unlock(&pm2->lock);
|
||||
|
||||
pm_runtime_put_sync(pm2->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -192,11 +178,22 @@ static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/* Disable SW EOC ctrl */
|
||||
ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW);
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable charging */
|
||||
ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
|
||||
(PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
|
||||
|
@ -216,21 +213,14 @@ int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
|
|||
|
||||
static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
|
||||
{
|
||||
int ret = 0;
|
||||
dev_err(pm2->dev, "Overvoltage detected\n");
|
||||
pm2->flags.ovv = true;
|
||||
power_supply_changed(&pm2->ac_chg.psy);
|
||||
|
||||
pm2->failure_input_ovv++;
|
||||
if (pm2->failure_input_ovv < 4) {
|
||||
ret = pm2xxx_charging_enable_mngt(pm2);
|
||||
goto out;
|
||||
} else {
|
||||
pm2->failure_input_ovv = 0;
|
||||
dev_err(pm2->dev, "Overvoltage detected\n");
|
||||
pm2->flags.ovv = true;
|
||||
power_supply_changed(&pm2->ac_chg.psy);
|
||||
}
|
||||
/* Schedule a new HW failure check */
|
||||
queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
|
||||
|
@ -245,13 +235,29 @@ static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
|
|||
|
||||
static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (val) {
|
||||
case PM2XXX_INT1_ITVBATLOWR:
|
||||
dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n");
|
||||
/* Enable SW EOC ctrl */
|
||||
ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
|
||||
PM2XXX_SWCTRL_SW);
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case PM2XXX_INT1_ITVBATLOWF:
|
||||
dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n");
|
||||
/* Disable SW EOC ctrl */
|
||||
ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
|
||||
PM2XXX_SWCTRL_HW);
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -322,16 +328,27 @@ static int pm2_int_reg0(void *pm2_data, int val)
|
|||
struct pm2xxx_charger *pm2 = pm2_data;
|
||||
int ret = 0;
|
||||
|
||||
if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
|
||||
ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val &
|
||||
(PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
|
||||
if (val & PM2XXX_INT1_ITVBATLOWR) {
|
||||
ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
|
||||
PM2XXX_INT1_ITVBATLOWR);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val & PM2XXX_INT1_ITVBATLOWF) {
|
||||
ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
|
||||
PM2XXX_INT1_ITVBATLOWF);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
|
||||
ret = pm2xxx_charger_bat_disc_mngt(pm2,
|
||||
PM2XXX_INT1_ITVBATDISCONNECT);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -447,7 +464,6 @@ static int pm2_int_reg5(void *pm2_data, int val)
|
|||
struct pm2xxx_charger *pm2 = pm2_data;
|
||||
int ret = 0;
|
||||
|
||||
|
||||
if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
|
||||
dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
|
||||
}
|
||||
|
@ -468,14 +484,22 @@ static irqreturn_t pm2xxx_irq_int(int irq, void *data)
|
|||
struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
|
||||
pm2xxx_reg_read(pm2,
|
||||
/* wake up the device */
|
||||
pm_runtime_get_sync(pm2->dev);
|
||||
|
||||
do {
|
||||
for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
|
||||
pm2xxx_reg_read(pm2,
|
||||
pm2xxx_interrupt_registers[i],
|
||||
&(interrupt->reg[i]));
|
||||
|
||||
if (interrupt->reg[i] > 0)
|
||||
interrupt->handler[i](pm2, interrupt->reg[i]);
|
||||
}
|
||||
if (interrupt->reg[i] > 0)
|
||||
interrupt->handler[i](pm2, interrupt->reg[i]);
|
||||
}
|
||||
} while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0);
|
||||
|
||||
pm_runtime_mark_last_busy(pm2->dev);
|
||||
pm_runtime_put_autosuspend(pm2->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -592,6 +616,8 @@ static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
|
|||
val->intval = POWER_SUPPLY_HEALTH_DEAD;
|
||||
else if (pm2->flags.main_thermal_prot)
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
else if (pm2->flags.ovv)
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
|
@ -674,10 +700,6 @@ static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
|
|||
ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
|
||||
PM2XXX_VBAT_LOW_MONITORING_ENA);
|
||||
|
||||
/* Disable LED */
|
||||
ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
|
||||
PM2XXX_LED_SELECT_DIS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -822,10 +844,54 @@ static void pm2xxx_charger_ac_work(struct work_struct *work)
|
|||
sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
|
||||
};
|
||||
|
||||
static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work)
|
||||
{
|
||||
u8 reg_value;
|
||||
|
||||
struct pm2xxx_charger *pm2 = container_of(work,
|
||||
struct pm2xxx_charger, check_hw_failure_work.work);
|
||||
|
||||
if (pm2->flags.ovv) {
|
||||
pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, ®_value);
|
||||
|
||||
if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV |
|
||||
PM2XXX_INT4_S_ITVPWR2OVV))) {
|
||||
pm2->flags.ovv = false;
|
||||
power_supply_changed(&pm2->ac_chg.psy);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we still have a failure, schedule a new check */
|
||||
if (pm2->flags.ovv) {
|
||||
queue_delayed_work(pm2->charger_wq,
|
||||
&pm2->check_hw_failure_work, round_jiffies(HZ));
|
||||
}
|
||||
}
|
||||
|
||||
static void pm2xxx_charger_check_main_thermal_prot_work(
|
||||
struct work_struct *work)
|
||||
{
|
||||
};
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger,
|
||||
check_main_thermal_prot_work);
|
||||
|
||||
/* Check if die temp warning is still active */
|
||||
ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE
|
||||
| PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE))
|
||||
pm2->flags.main_thermal_prot = true;
|
||||
else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL
|
||||
| PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL))
|
||||
pm2->flags.main_thermal_prot = false;
|
||||
|
||||
power_supply_changed(&pm2->ac_chg.psy);
|
||||
}
|
||||
|
||||
static struct pm2xxx_interrupts pm2xxx_int = {
|
||||
.handler[0] = pm2_int_reg0,
|
||||
|
@ -842,22 +908,92 @@ static struct pm2xxx_irq pm2xxx_charger_irq[] = {
|
|||
|
||||
static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client)
|
||||
{
|
||||
struct pm2xxx_charger *pm2;
|
||||
|
||||
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
|
||||
set_lpn_pin(pm2);
|
||||
|
||||
/* If we still have a HW failure, schedule a new check */
|
||||
if (pm2->flags.ovv)
|
||||
queue_delayed_work(pm2->charger_wq,
|
||||
&pm2->check_hw_failure_work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct pm2xxx_charger *pm2;
|
||||
|
||||
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
|
||||
clear_lpn_pin(pm2);
|
||||
|
||||
/* Cancel any pending HW failure check */
|
||||
if (delayed_work_pending(&pm2->check_hw_failure_work))
|
||||
cancel_delayed_work(&pm2->check_hw_failure_work);
|
||||
|
||||
flush_work(&pm2->ac_work);
|
||||
flush_work(&pm2->check_main_thermal_prot_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
||||
#ifdef CONFIG_PM
|
||||
static int pm2xxx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
|
||||
struct pm2xxx_charger *pm2;
|
||||
int ret = 0;
|
||||
|
||||
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
|
||||
if (!pm2) {
|
||||
dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
clear_lpn_pin(pm2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pm2xxx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
|
||||
struct pm2xxx_charger *pm2;
|
||||
int ret = 0;
|
||||
|
||||
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
|
||||
if (!pm2) {
|
||||
dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
|
||||
set_lpn_pin(pm2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops pm2xxx_pm_ops = {
|
||||
.runtime_suspend = pm2xxx_runtime_suspend,
|
||||
.runtime_resume = pm2xxx_runtime_resume,
|
||||
};
|
||||
#define PM2XXX_PM_OPS (&pm2xxx_pm_ops)
|
||||
#else
|
||||
#define PM2XXX_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data;
|
||||
struct pm2xxx_charger *pm2;
|
||||
int ret = 0;
|
||||
u8 val;
|
||||
int i;
|
||||
|
||||
pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
|
||||
if (!pm2) {
|
||||
|
@ -867,7 +1003,6 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
|||
|
||||
/* get parent data */
|
||||
pm2->dev = &i2c_client->dev;
|
||||
pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
|
||||
|
||||
pm2->pm2_int = &pm2xxx_int;
|
||||
|
||||
|
@ -889,14 +1024,6 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
|||
|
||||
pm2->bat = pl_data->battery;
|
||||
|
||||
/*get lpn GPIO from platform data*/
|
||||
if (!pm2->pdata->lpn_gpio) {
|
||||
dev_err(pm2->dev, "no lpn gpio data supplied\n");
|
||||
ret = -EINVAL;
|
||||
goto free_device_info;
|
||||
}
|
||||
pm2->lpn_pin = pm2->pdata->lpn_gpio;
|
||||
|
||||
if (!i2c_check_functionality(i2c_client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
|
||||
|
@ -945,6 +1072,10 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
|||
INIT_WORK(&pm2->check_main_thermal_prot_work,
|
||||
pm2xxx_charger_check_main_thermal_prot_work);
|
||||
|
||||
/* Init work for HW failure check */
|
||||
INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work,
|
||||
pm2xxx_charger_check_hw_failure_work);
|
||||
|
||||
/*
|
||||
* VDD ADC supply needs to be enabled from this driver when there
|
||||
* is a charger connected to avoid erroneous BTEMP_HIGH/LOW
|
||||
|
@ -965,40 +1096,72 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
|||
}
|
||||
|
||||
/* Register interrupts */
|
||||
ret = request_threaded_irq(pm2->pdata->irq_number, NULL,
|
||||
ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number),
|
||||
NULL,
|
||||
pm2xxx_charger_irq[0].isr,
|
||||
pm2->pdata->irq_type,
|
||||
pm2xxx_charger_irq[0].name, pm2);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n",
|
||||
pm2xxx_charger_irq[0].name, pm2->pdata->irq_number, ret);
|
||||
pm2xxx_charger_irq[0].name,
|
||||
gpio_to_irq(pm2->pdata->gpio_irq_number), ret);
|
||||
goto unregister_pm2xxx_charger;
|
||||
}
|
||||
|
||||
/*Initialize lock*/
|
||||
ret = pm_runtime_set_active(pm2->dev);
|
||||
if (ret)
|
||||
dev_err(pm2->dev, "set active Error\n");
|
||||
|
||||
pm_runtime_enable(pm2->dev);
|
||||
pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(pm2->dev);
|
||||
pm_runtime_resume(pm2->dev);
|
||||
|
||||
/* pm interrupt can wake up system */
|
||||
ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
|
||||
if (ret) {
|
||||
dev_err(pm2->dev, "failed to set irq wake\n");
|
||||
goto unregister_pm2xxx_interrupt;
|
||||
}
|
||||
|
||||
mutex_init(&pm2->lock);
|
||||
|
||||
/*
|
||||
* Charger detection mechanism requires pulling up the LPN pin
|
||||
* while i2c communication if Charger is not connected
|
||||
* LPN pin of PM2301 is GPIO60 of AB9540
|
||||
*/
|
||||
ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n");
|
||||
goto unregister_pm2xxx_charger;
|
||||
}
|
||||
ret = gpio_direction_output(pm2->lpn_pin, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
|
||||
goto free_gpio;
|
||||
if (gpio_is_valid(pm2->pdata->lpn_gpio)) {
|
||||
/* get lpn GPIO from platform data */
|
||||
pm2->lpn_pin = pm2->pdata->lpn_gpio;
|
||||
|
||||
/*
|
||||
* Charger detection mechanism requires pulling up the LPN pin
|
||||
* while i2c communication if Charger is not connected
|
||||
* LPN pin of PM2301 is GPIO60 of AB9540
|
||||
*/
|
||||
ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n");
|
||||
goto disable_pm2_irq_wake;
|
||||
}
|
||||
ret = gpio_direction_output(pm2->lpn_pin, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
|
||||
goto free_gpio;
|
||||
}
|
||||
set_lpn_pin(pm2);
|
||||
}
|
||||
|
||||
/* read interrupt registers */
|
||||
for (i = 0; i < PM2XXX_NUM_INT_REG; i++)
|
||||
pm2xxx_reg_read(pm2,
|
||||
pm2xxx_interrupt_registers[i],
|
||||
&val);
|
||||
|
||||
ret = pm2xxx_charger_detection(pm2, &val);
|
||||
|
||||
if ((ret == 0) && val) {
|
||||
pm2->ac.charger_connected = 1;
|
||||
ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
|
||||
AB8500_MAIN_CH_DET);
|
||||
pm2->ac_conn = true;
|
||||
power_supply_changed(&pm2->ac_chg.psy);
|
||||
sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
|
||||
|
@ -1007,7 +1170,13 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
|
|||
return 0;
|
||||
|
||||
free_gpio:
|
||||
gpio_free(pm2->lpn_pin);
|
||||
if (gpio_is_valid(pm2->lpn_pin))
|
||||
gpio_free(pm2->lpn_pin);
|
||||
disable_pm2_irq_wake:
|
||||
disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
|
||||
unregister_pm2xxx_interrupt:
|
||||
/* disable interrupt */
|
||||
free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
|
||||
unregister_pm2xxx_charger:
|
||||
/* unregister power supply */
|
||||
power_supply_unregister(&pm2->ac_chg.psy);
|
||||
|
@ -1018,18 +1187,24 @@ free_charger_wq:
|
|||
destroy_workqueue(pm2->charger_wq);
|
||||
free_device_info:
|
||||
kfree(pm2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
|
||||
static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
|
||||
{
|
||||
struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
|
||||
|
||||
/* Disable pm_runtime */
|
||||
pm_runtime_disable(pm2->dev);
|
||||
/* Disable AC charging */
|
||||
pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
|
||||
|
||||
/* Disable wake by pm interrupt */
|
||||
disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
|
||||
|
||||
/* Disable interrupts */
|
||||
free_irq(pm2->pdata->irq_number, pm2);
|
||||
free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(pm2->charger_wq);
|
||||
|
@ -1041,8 +1216,8 @@ static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
|
|||
|
||||
power_supply_unregister(&pm2->ac_chg.psy);
|
||||
|
||||
/*Free GPIO60*/
|
||||
gpio_free(pm2->lpn_pin);
|
||||
if (gpio_is_valid(pm2->lpn_pin))
|
||||
gpio_free(pm2->lpn_pin);
|
||||
|
||||
kfree(pm2);
|
||||
|
||||
|
@ -1058,12 +1233,13 @@ MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
|
|||
|
||||
static struct i2c_driver pm2xxx_charger_driver = {
|
||||
.probe = pm2xxx_wall_charger_probe,
|
||||
.remove = __devexit_p(pm2xxx_wall_charger_remove),
|
||||
.remove = pm2xxx_wall_charger_remove,
|
||||
.suspend = pm2xxx_wall_charger_suspend,
|
||||
.resume = pm2xxx_wall_charger_resume,
|
||||
.driver = {
|
||||
.name = "pm2xxx-wall_charger",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = PM2XXX_PM_OPS,
|
||||
},
|
||||
.id_table = pm2xxx_id,
|
||||
};
|
||||
|
@ -1078,11 +1254,10 @@ static void __exit pm2xxx_charger_exit(void)
|
|||
i2c_del_driver(&pm2xxx_charger_driver);
|
||||
}
|
||||
|
||||
subsys_initcall_sync(pm2xxx_charger_init);
|
||||
device_initcall_sync(pm2xxx_charger_init);
|
||||
module_exit(pm2xxx_charger_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
|
||||
MODULE_ALIAS("platform:pm2xxx-charger");
|
||||
MODULE_DESCRIPTION("PM2xxx charger management driver");
|
||||
|
||||
|
|
|
@ -9,27 +9,6 @@
|
|||
#ifndef PM2301_CHARGER_H
|
||||
#define PM2301_CHARGER_H
|
||||
|
||||
#define MAIN_WDOG_ENA 0x01
|
||||
#define MAIN_WDOG_KICK 0x02
|
||||
#define MAIN_WDOG_DIS 0x00
|
||||
#define CHARG_WD_KICK 0x01
|
||||
#define MAIN_CH_ENA 0x01
|
||||
#define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02
|
||||
#define MAIN_CH_DET 0x01
|
||||
#define MAIN_CH_CV_ON 0x04
|
||||
#define OTP_ENABLE_WD 0x01
|
||||
|
||||
#define MAIN_CH_INPUT_CURR_SHIFT 4
|
||||
|
||||
#define LED_INDICATOR_PWM_ENA 0x01
|
||||
#define LED_INDICATOR_PWM_DIS 0x00
|
||||
#define LED_IND_CUR_5MA 0x04
|
||||
#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
|
||||
|
||||
/* HW failure constants */
|
||||
#define MAIN_CH_TH_PROT 0x02
|
||||
#define MAIN_CH_NOK 0x01
|
||||
|
||||
/* Watchdog timeout constant */
|
||||
#define WD_TIMER 0x30 /* 4min */
|
||||
#define WD_KICK_INTERVAL (30 * HZ)
|
||||
|
@ -495,7 +474,6 @@ struct pm2xxx_charger {
|
|||
int failure_input_ovv;
|
||||
unsigned int lpn_pin;
|
||||
struct pm2xxx_interrupts *pm2_int;
|
||||
struct ab8500_gpadc *gpadc;
|
||||
struct regulator *regu;
|
||||
struct pm2xxx_bm_data *bat;
|
||||
struct mutex lock;
|
||||
|
@ -506,6 +484,7 @@ struct pm2xxx_charger {
|
|||
struct delayed_work check_vbat_work;
|
||||
struct work_struct ac_work;
|
||||
struct work_struct check_main_thermal_prot_work;
|
||||
struct delayed_work check_hw_failure_work;
|
||||
struct ux500_charger ac_chg;
|
||||
struct pm2xxx_charger_event_flags flags;
|
||||
};
|
||||
|
|
|
@ -89,6 +89,11 @@ struct abx500_fg;
|
|||
* points.
|
||||
* @maint_thres This is the threshold where we stop reporting
|
||||
* battery full while in maintenance, in per cent
|
||||
* @pcut_enable: Enable power cut feature in ab8505
|
||||
* @pcut_max_time: Max time threshold
|
||||
* @pcut_flag_time: Flagtime threshold
|
||||
* @pcut_max_restart: Max number of restarts
|
||||
* @pcut_debounce_time: Sets battery debounce time
|
||||
*/
|
||||
struct abx500_fg_parameters {
|
||||
int recovery_sleep_timer;
|
||||
|
@ -106,6 +111,11 @@ struct abx500_fg_parameters {
|
|||
int battok_raising_th_sel1;
|
||||
int user_cap_limit;
|
||||
int maint_thres;
|
||||
bool pcut_enable;
|
||||
u8 pcut_max_time;
|
||||
u8 pcut_flag_time;
|
||||
u8 pcut_max_restart;
|
||||
u8 pcut_debounce_time;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -236,7 +246,11 @@ struct abx500_bm_charger_parameters {
|
|||
* @interval_not_charging charge alg cycle period time when not charging (sec)
|
||||
* @temp_hysteresis temperature hysteresis
|
||||
* @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
|
||||
* @maxi: maximization parameters
|
||||
* @n_chg_out_curr number of elements in array chg_output_curr
|
||||
* @n_chg_in_curr number of elements in array chg_input_curr
|
||||
* @chg_output_curr charger output current level map
|
||||
* @chg_input_curr charger input current level map
|
||||
* @maxi maximization parameters
|
||||
* @cap_levels capacity in percent for the different capacity levels
|
||||
* @bat_type table of supported battery types
|
||||
* @chg_params charger parameters
|
||||
|
@ -257,6 +271,7 @@ struct abx500_bm_data {
|
|||
bool autopower_cfg;
|
||||
bool ac_enabled;
|
||||
bool usb_enabled;
|
||||
bool usb_power_path;
|
||||
bool no_maintenance;
|
||||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
|
@ -270,6 +285,10 @@ struct abx500_bm_data {
|
|||
int interval_not_charging;
|
||||
int temp_hysteresis;
|
||||
int gnd_lift_resistance;
|
||||
int n_chg_out_curr;
|
||||
int n_chg_in_curr;
|
||||
int *chg_output_curr;
|
||||
int *chg_input_curr;
|
||||
const struct abx500_maxim_parameters *maxi;
|
||||
const struct abx500_bm_capacity_levels *cap_levels;
|
||||
struct abx500_battery_type *bat_type;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* Bank : 0x5
|
||||
*/
|
||||
#define AB8500_USB_LINE_STAT_REG 0x80
|
||||
#define AB8500_USB_LINE_CTRL2_REG 0x82
|
||||
#define AB8500_USB_LINK1_STAT_REG 0x94
|
||||
|
||||
/*
|
||||
|
@ -33,7 +34,7 @@
|
|||
#define AB8500_CH_STATUS2_REG 0x01
|
||||
#define AB8500_CH_USBCH_STAT1_REG 0x02
|
||||
#define AB8500_CH_USBCH_STAT2_REG 0x03
|
||||
#define AB8500_CH_FSM_STAT_REG 0x04
|
||||
#define AB8540_CH_USBCH_STAT3_REG 0x04
|
||||
#define AB8500_CH_STAT_REG 0x05
|
||||
|
||||
/*
|
||||
|
@ -69,6 +70,8 @@
|
|||
#define AB8500_USBCH_CTRL1_REG 0xC0
|
||||
#define AB8500_USBCH_CTRL2_REG 0xC1
|
||||
#define AB8500_USBCH_IPT_CRNTLVL_REG 0xC2
|
||||
#define AB8540_USB_PP_MODE_REG 0xC5
|
||||
#define AB8540_USB_PP_CHR_REG 0xC6
|
||||
|
||||
/*
|
||||
* Gas Gauge register offsets
|
||||
|
@ -105,6 +108,7 @@
|
|||
#define AB8500_RTC_BACKUP_CHG_REG 0x0C
|
||||
#define AB8500_RTC_CC_CONF_REG 0x01
|
||||
#define AB8500_RTC_CTRL_REG 0x0B
|
||||
#define AB8500_RTC_CTRL1_REG 0x11
|
||||
|
||||
/*
|
||||
* OTP register offsets
|
||||
|
@ -154,6 +158,7 @@
|
|||
#define CH_OP_CUR_LVL_1P4 0x0D
|
||||
#define CH_OP_CUR_LVL_1P5 0x0E
|
||||
#define CH_OP_CUR_LVL_1P6 0x0F
|
||||
#define CH_OP_CUR_LVL_2P 0x3F
|
||||
|
||||
/* BTEMP High thermal limits */
|
||||
#define BTEMP_HIGH_TH_57_0 0x00
|
||||
|
@ -179,10 +184,25 @@
|
|||
#define BUP_ICH_SEL_300UA 0x08
|
||||
#define BUP_ICH_SEL_700UA 0x0C
|
||||
|
||||
#define BUP_VCH_SEL_2P5V 0x00
|
||||
#define BUP_VCH_SEL_2P6V 0x01
|
||||
#define BUP_VCH_SEL_2P8V 0x02
|
||||
#define BUP_VCH_SEL_3P1V 0x03
|
||||
enum bup_vch_sel {
|
||||
BUP_VCH_SEL_2P5V,
|
||||
BUP_VCH_SEL_2P6V,
|
||||
BUP_VCH_SEL_2P8V,
|
||||
BUP_VCH_SEL_3P1V,
|
||||
/*
|
||||
* Note that the following 5 values 2.7v, 2.9v, 3.0v, 3.2v, 3.3v
|
||||
* are only available on ab8540. You can't choose these 5
|
||||
* voltage on ab8500/ab8505/ab9540.
|
||||
*/
|
||||
BUP_VCH_SEL_2P7V,
|
||||
BUP_VCH_SEL_2P9V,
|
||||
BUP_VCH_SEL_3P0V,
|
||||
BUP_VCH_SEL_3P2V,
|
||||
BUP_VCH_SEL_3P3V,
|
||||
};
|
||||
|
||||
#define BUP_VCH_RANGE 0x02
|
||||
#define VBUP33_VRTCN 0x01
|
||||
|
||||
/* Battery OVV constants */
|
||||
#define BATT_OVV_ENA 0x02
|
||||
|
@ -228,6 +248,8 @@
|
|||
#define BAT_CTRL_20U_ENA 0x02
|
||||
#define BAT_CTRL_18U_ENA 0x01
|
||||
#define BAT_CTRL_16U_ENA 0x02
|
||||
#define BAT_CTRL_60U_ENA 0x01
|
||||
#define BAT_CTRL_120U_ENA 0x02
|
||||
#define BAT_CTRL_CMP_ENA 0x04
|
||||
#define FORCE_BAT_CTRL_CMP_HIGH 0x08
|
||||
#define BAT_CTRL_PULL_UP_ENA 0x10
|
||||
|
@ -235,6 +257,24 @@
|
|||
/* Battery type */
|
||||
#define BATTERY_UNKNOWN 00
|
||||
|
||||
/* Registers for pcut feature in ab8505 and ab9540 */
|
||||
#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
|
||||
#define AB8505_RTC_PCUT_TIME_REG 0x13
|
||||
#define AB8505_RTC_PCUT_MAX_TIME_REG 0x14
|
||||
#define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15
|
||||
#define AB8505_RTC_PCUT_RESTART_REG 0x16
|
||||
#define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17
|
||||
|
||||
/* USB Power Path constants for ab8540 */
|
||||
#define BUS_VSYS_VOL_SELECT_MASK 0x06
|
||||
#define BUS_VSYS_VOL_SELECT_3P6V 0x00
|
||||
#define BUS_VSYS_VOL_SELECT_3P325V 0x02
|
||||
#define BUS_VSYS_VOL_SELECT_3P9V 0x04
|
||||
#define BUS_VSYS_VOL_SELECT_4P3V 0x06
|
||||
#define BUS_POWER_PATH_MODE_ENA 0x01
|
||||
#define BUS_PP_PRECHG_CURRENT_MASK 0x0E
|
||||
#define BUS_POWER_PATH_PRECHG_ENA 0x01
|
||||
|
||||
/**
|
||||
* struct res_to_temp - defines one point in a temp to res curve. To
|
||||
* be used in battery packs that combines the identification resistor with a
|
||||
|
@ -283,6 +323,11 @@ struct ab8500_fg;
|
|||
* points.
|
||||
* @maint_thres This is the threshold where we stop reporting
|
||||
* battery full while in maintenance, in per cent
|
||||
* @pcut_enable: Enable power cut feature in ab8505
|
||||
* @pcut_max_time: Max time threshold
|
||||
* @pcut_flag_time: Flagtime threshold
|
||||
* @pcut_max_restart: Max number of restarts
|
||||
* @pcut_debunce_time: Sets battery debounce time
|
||||
*/
|
||||
struct ab8500_fg_parameters {
|
||||
int recovery_sleep_timer;
|
||||
|
@ -299,6 +344,11 @@ struct ab8500_fg_parameters {
|
|||
int battok_raising_th_sel1;
|
||||
int user_cap_limit;
|
||||
int maint_thres;
|
||||
bool pcut_enable;
|
||||
u8 pcut_max_time;
|
||||
u8 pcut_flag_time;
|
||||
u8 pcut_max_restart;
|
||||
u8 pcut_debunce_time;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,32 +4,72 @@
|
|||
*
|
||||
* Author: Arun R Murthy <arun.murthy@stericsson.com>
|
||||
* Author: Daniel Willerud <daniel.willerud@stericsson.com>
|
||||
* Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com>
|
||||
*/
|
||||
|
||||
#ifndef _AB8500_GPADC_H
|
||||
#define _AB8500_GPADC_H
|
||||
|
||||
/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2) */
|
||||
#define BAT_CTRL 0x01
|
||||
#define BTEMP_BALL 0x02
|
||||
#define MAIN_CHARGER_V 0x03
|
||||
#define ACC_DETECT1 0x04
|
||||
#define ACC_DETECT2 0x05
|
||||
#define ADC_AUX1 0x06
|
||||
#define ADC_AUX2 0x07
|
||||
#define MAIN_BAT_V 0x08
|
||||
#define VBUS_V 0x09
|
||||
#define MAIN_CHARGER_C 0x0A
|
||||
#define USB_CHARGER_C 0x0B
|
||||
#define BK_BAT_V 0x0C
|
||||
#define DIE_TEMP 0x0D
|
||||
/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2
|
||||
* and ADCHwSel[4:0] in GPADCCtrl3 ) */
|
||||
#define BAT_CTRL 0x01
|
||||
#define BTEMP_BALL 0x02
|
||||
#define MAIN_CHARGER_V 0x03
|
||||
#define ACC_DETECT1 0x04
|
||||
#define ACC_DETECT2 0x05
|
||||
#define ADC_AUX1 0x06
|
||||
#define ADC_AUX2 0x07
|
||||
#define MAIN_BAT_V 0x08
|
||||
#define VBUS_V 0x09
|
||||
#define MAIN_CHARGER_C 0x0A
|
||||
#define USB_CHARGER_C 0x0B
|
||||
#define BK_BAT_V 0x0C
|
||||
#define DIE_TEMP 0x0D
|
||||
#define USB_ID 0x0E
|
||||
#define XTAL_TEMP 0x12
|
||||
#define VBAT_TRUE_MEAS 0x13
|
||||
#define BAT_CTRL_AND_IBAT 0x1C
|
||||
#define VBAT_MEAS_AND_IBAT 0x1D
|
||||
#define VBAT_TRUE_MEAS_AND_IBAT 0x1E
|
||||
#define BAT_TEMP_AND_IBAT 0x1F
|
||||
|
||||
/* Virtual channel used only for ibat convertion to ampere
|
||||
* Battery current conversion (ibat) cannot be requested as a single conversion
|
||||
* but it is always in combination with other input requests
|
||||
*/
|
||||
#define IBAT_VIRTUAL_CHANNEL 0xFF
|
||||
|
||||
#define SAMPLE_1 1
|
||||
#define SAMPLE_4 4
|
||||
#define SAMPLE_8 8
|
||||
#define SAMPLE_16 16
|
||||
#define RISING_EDGE 0
|
||||
#define FALLING_EDGE 1
|
||||
|
||||
/* Arbitrary ADC conversion type constants */
|
||||
#define ADC_SW 0
|
||||
#define ADC_HW 1
|
||||
|
||||
struct ab8500_gpadc;
|
||||
|
||||
struct ab8500_gpadc *ab8500_gpadc_get(char *name);
|
||||
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel);
|
||||
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel);
|
||||
int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
|
||||
static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
|
||||
{
|
||||
return ab8500_gpadc_sw_hw_convert(gpadc, channel,
|
||||
SAMPLE_16, 0, 0, ADC_SW);
|
||||
}
|
||||
|
||||
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
|
||||
int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
|
||||
int *ibat);
|
||||
int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc,
|
||||
u8 channel, int ad_value);
|
||||
u8 channel, int ad_value);
|
||||
void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
|
||||
u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
|
||||
u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h);
|
||||
|
||||
#endif /* _AB8500_GPADC_H */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
int ab8500_sysctrl_read(u16 reg, u8 *value);
|
||||
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value);
|
||||
void ab8500_restart(char mode, const char *cmd);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -40,6 +41,7 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
|
|||
/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
|
||||
struct ab8500_sysctrl_platform_data {
|
||||
u8 initial_req_buf_config[8];
|
||||
u16 (*reboot_reason_code)(const char *cmd);
|
||||
};
|
||||
|
||||
/* Registers */
|
||||
|
@ -299,4 +301,8 @@ struct ab8500_sysctrl_platform_data {
|
|||
#define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_MASK 0xFF
|
||||
#define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_SHIFT 0
|
||||
|
||||
#define AB8500_ENABLE_WD 0x1
|
||||
#define AB8500_KICK_WD 0x2
|
||||
#define AB8500_WD_RESTART_ON_EXPIRE 0x10
|
||||
|
||||
#endif /* __AB8500_SYSCTRL_H */
|
||||
|
|
|
@ -362,6 +362,7 @@ struct ab8500 {
|
|||
u8 *oldmask;
|
||||
int mask_size;
|
||||
const int *irq_reg_offset;
|
||||
int it_latchhier_num;
|
||||
};
|
||||
|
||||
struct regulator_reg_init;
|
||||
|
@ -512,6 +513,8 @@ static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab)
|
|||
return (is_ab9540(ab) && (ab->chip_id < AB8500_CUT2P0));
|
||||
}
|
||||
|
||||
void ab8500_override_turn_on_stat(u8 mask, u8 set);
|
||||
|
||||
#ifdef CONFIG_AB8500_DEBUG
|
||||
void ab8500_dump_all_banks(struct device *dev);
|
||||
void ab8500_debug_register_interrupt(int line);
|
||||
|
|
|
@ -17,8 +17,11 @@ struct ux500_charger;
|
|||
|
||||
struct ux500_charger_ops {
|
||||
int (*enable) (struct ux500_charger *, int, int, int);
|
||||
int (*check_enable) (struct ux500_charger *, int, int);
|
||||
int (*kick_wd) (struct ux500_charger *);
|
||||
int (*update_curr) (struct ux500_charger *, int);
|
||||
int (*pp_enable) (struct ux500_charger *, bool);
|
||||
int (*pre_chg_enable) (struct ux500_charger *, bool);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -29,6 +32,7 @@ struct ux500_charger_ops {
|
|||
* @max_out_curr maximum output charger current in mA
|
||||
* @enabled indicates if this charger is used or not
|
||||
* @external external charger unit (pm2xxx)
|
||||
* @power_path USB power path support
|
||||
*/
|
||||
struct ux500_charger {
|
||||
struct power_supply psy;
|
||||
|
@ -38,6 +42,9 @@ struct ux500_charger {
|
|||
int wdt_refresh;
|
||||
bool enabled;
|
||||
bool external;
|
||||
bool power_path;
|
||||
};
|
||||
|
||||
extern struct blocking_notifier_head charger_notifier_list;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -48,7 +48,7 @@ struct pm2xxx_charger_platform_data {
|
|||
size_t num_supplicants;
|
||||
int i2c_bus;
|
||||
const char *label;
|
||||
int irq_number;
|
||||
int gpio_irq_number;
|
||||
unsigned int lpn_gpio;
|
||||
int irq_type;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue