static int twl4030_charger_update_current()

in supply/twl4030_charger.c [238:395]


static int twl4030_charger_update_current(struct twl4030_bci *bci)
{
	int status;
	int cur;
	unsigned reg, cur_reg;
	u8 bcictl1, oldreg, fullreg;
	bool cgain = false;
	u8 boot_bci;

	/*
	 * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
	 * and AC is enabled, set current for 'ac'
	 */
	if (ac_available(bci->channel_vac)) {
		cur = bci->ac_cur;
		bci->ac_is_active = true;
	} else {
		cur = bci->usb_cur;
		bci->ac_is_active = false;
		if (cur > bci->usb_cur_target) {
			cur = bci->usb_cur_target;
			bci->usb_cur = cur;
		}
		if (cur < bci->usb_cur_target)
			schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
	}

	/* First, check thresholds and see if cgain is needed */
	if (bci->ichg_eoc >= 200000)
		cgain = true;
	if (bci->ichg_lo >= 400000)
		cgain = true;
	if (bci->ichg_hi >= 820000)
		cgain = true;
	if (cur > 852000)
		cgain = true;

	status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
	if (status < 0)
		return status;
	if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
			    TWL4030_PM_MASTER_BOOT_BCI) < 0)
		boot_bci = 0;
	boot_bci &= 7;

	if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
		/* Need to turn for charging while we change the
		 * CGAIN bit.  Leave it off while everything is
		 * updated.
		 */
		twl4030_clear_set_boot_bci(boot_bci, 0);

	/*
	 * For ichg_eoc, the hardware only supports reg values matching
	 * 100XXXX000, and requires the XXXX be stored in the high nibble
	 * of TWL4030_BCIMFTH8.
	 */
	reg = ua2regval(bci->ichg_eoc, cgain);
	if (reg > 0x278)
		reg = 0x278;
	if (reg < 0x200)
		reg = 0x200;
	reg = (reg >> 3) & 0xf;
	fullreg = reg << 4;

	/*
	 * For ichg_lo, reg value must match 10XXXX0000.
	 * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
	 */
	reg = ua2regval(bci->ichg_lo, cgain);
	if (reg > 0x2F0)
		reg = 0x2F0;
	if (reg < 0x200)
		reg = 0x200;
	reg = (reg >> 4) & 0xf;
	fullreg |= reg;

	/* ichg_eoc and ichg_lo live in same register */
	status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
	if (status < 0)
		return status;
	if (oldreg != fullreg) {
		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
					  TWL4030_BCIMFKEY);
		if (status < 0)
			return status;
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
				 fullreg, TWL4030_BCIMFTH8);
	}

	/* ichg_hi threshold must be 1XXXX01100 (I think) */
	reg = ua2regval(bci->ichg_hi, cgain);
	if (reg > 0x3E0)
		reg = 0x3E0;
	if (reg < 0x200)
		reg = 0x200;
	fullreg = (reg >> 5) & 0xF;
	fullreg <<= 4;
	status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
	if (status < 0)
		return status;
	if ((oldreg & 0xF0) != fullreg) {
		fullreg |= (oldreg & 0x0F);
		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
					  TWL4030_BCIMFKEY);
		if (status < 0)
			return status;
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
				 fullreg, TWL4030_BCIMFTH9);
	}

	/*
	 * And finally, set the current.  This is stored in
	 * two registers.
	 */
	reg = ua2regval(cur, cgain);
	/* we have only 10 bits */
	if (reg > 0x3ff)
		reg = 0x3ff;
	status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
	if (status < 0)
		return status;
	cur_reg = oldreg;
	status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
	if (status < 0)
		return status;
	cur_reg |= oldreg << 8;
	if (reg != oldreg) {
		/* disable write protection for one write access for
		 * BCIIREF */
		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
					  TWL4030_BCIMFKEY);
		if (status < 0)
			return status;
		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
					  (reg & 0x100) ? 3 : 2,
					  TWL4030_BCIIREF2);
		if (status < 0)
			return status;
		/* disable write protection for one write access for
		 * BCIIREF */
		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
					  TWL4030_BCIMFKEY);
		if (status < 0)
			return status;
		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
					  reg & 0xff,
					  TWL4030_BCIIREF1);
	}
	if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
		/* Flip CGAIN and re-enable charging */
		bcictl1 ^= TWL4030_CGAIN;
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
				 bcictl1, TWL4030_BCICTL1);
		twl4030_clear_set_boot_bci(0, boot_bci);
	}
	return 0;
}