/* * Copyright (C) 2012 Samsung Electronics * Lukasz Majewski * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include static int fg_write_regs(struct pmic *p, u8 addr, u16 *data, int num) { int ret = 0; int i; for (i = 0; i < num; i++, addr++) { ret = pmic_reg_write(p, addr, *(data + i)); if (ret) return ret; } return 0; } static int fg_read_regs(struct pmic *p, u8 addr, u16 *data, int num) { unsigned int dat; int ret = 0; int i; for (i = 0; i < num; i++, addr++) { ret = pmic_reg_read(p, addr, &dat); if (ret) return ret; *(data + i) = (u16)dat; } return 0; } static int fg_write_and_verify(struct pmic *p, u8 addr, u16 data) { unsigned int val = data; int ret = 0; ret |= pmic_reg_write(p, addr, val); ret |= pmic_reg_read(p, addr, &val); if (ret) return ret; if (((u16) val) == data) return 0; return -1; } static void por_fuelgauge_init(struct pmic *p) { u16 r_data0[16], r_data1[16], r_data2[16]; u32 rewrite_count = 5; u32 check_count; u32 lock_count; u32 i = 0; u32 val; s32 ret = 0; char *status_msg; /* Delay 500 ms */ mdelay(500); /* Initilize Configuration */ pmic_reg_write(p, MAX17042_CONFIG, 0x2310); rewrite_model: check_count = 5; lock_count = 5; if (!rewrite_count--) { status_msg = "init failed!"; goto error; } /* Unlock Model Access */ pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_UNLOCK1); pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_UNLOCK2); /* Write/Read/Verify the Custom Model */ ret = fg_write_regs(p, MAX17042_MODEL1, cell_character0, ARRAY_SIZE(cell_character0)); if (ret) goto rewrite_model; ret = fg_write_regs(p, MAX17042_MODEL2, cell_character1, ARRAY_SIZE(cell_character1)); if (ret) goto rewrite_model; ret = fg_write_regs(p, MAX17042_MODEL3, cell_character2, ARRAY_SIZE(cell_character2)); if (ret) goto rewrite_model; check_model: if (!check_count--) { if (rewrite_count) goto rewrite_model; else status_msg = "check failed!"; goto error; } ret = fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); if (ret) goto check_model; ret = fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); if (ret) goto check_model; ret = fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); if (ret) goto check_model; for (i = 0; i < 16; i++) { if ((cell_character0[i] != r_data0[i]) || (cell_character1[i] != r_data1[i]) || (cell_character2[i] != r_data2[i])) goto rewrite_model; } lock_model: if (!lock_count--) { if (rewrite_count) goto rewrite_model; else status_msg = "lock failed!"; goto error; } /* Lock model access */ pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_LOCK1); pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_LOCK2); /* Verify the model access is locked */ ret = fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); if (ret) goto lock_model; ret = fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); if (ret) goto lock_model; ret = fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); if (ret) goto lock_model; for (i = 0; i < ARRAY_SIZE(r_data0); i++) { /* Check if model locked */ if (r_data0[i] || r_data1[i] || r_data2[i]) goto lock_model; } /* Write Custom Parameters */ fg_write_and_verify(p, MAX17042_RCOMP0, RCOMP0); fg_write_and_verify(p, MAX17042_TEMPCO, TempCo); /* Delay at least 350mS */ mdelay(350); /* Initialization Complete */ pmic_reg_read(p, MAX17042_STATUS, &val); /* Write and Verify Status with POR bit Cleared */ fg_write_and_verify(p, MAX17042_STATUS, val & ~MAX17042_POR); /* Delay at least 350 ms */ mdelay(350); status_msg = "OK!"; error: debug("%s: model init status: %s\n", p->name, status_msg); return; } static int power_update_battery(struct pmic *p, struct pmic *bat) { struct power_battery *pb = bat->pbat; unsigned int val; int ret = 0; if (pmic_probe(p)) { puts("Can't find max17042 fuel gauge\n"); return -1; } ret |= pmic_reg_read(p, MAX17042_VFSOC, &val); pb->bat->state_of_chrg = (val >> 8); pmic_reg_read(p, MAX17042_VCELL, &val); debug("vfsoc: 0x%x\n", val); pb->bat->voltage_uV = ((val & 0xFFUL) >> 3) + ((val & 0xFF00) >> 3); pb->bat->voltage_uV = (pb->bat->voltage_uV * 625); pmic_reg_read(p, 0x05, &val); pb->bat->capacity = val >> 2; return ret; } static int power_check_battery(struct pmic *p, struct pmic *bat) { struct power_battery *pb = bat->pbat; unsigned int val; int ret = 0; if (pmic_probe(p)) { puts("Can't find max17042 fuel gauge\n"); return -1; } ret |= pmic_reg_read(p, MAX17042_STATUS, &val); debug("fg status: 0x%x\n", val); if (val & MAX17042_POR) por_fuelgauge_init(p); ret |= pmic_reg_read(p, MAX17042_VERSION, &val); pb->bat->version = val; power_update_battery(p, bat); debug("fg ver: 0x%x\n", pb->bat->version); printf("BAT: state_of_charge(SOC):%d%%\n", pb->bat->state_of_chrg); printf(" voltage: %d.%6.6d [V] (expected to be %d [mAh])\n", pb->bat->voltage_uV / 1000000, pb->bat->voltage_uV % 1000000, pb->bat->capacity); if (pb->bat->voltage_uV > 3850000) pb->bat->state = EXT_SOURCE; else if (pb->bat->voltage_uV < 3600000 || pb->bat->state_of_chrg < 5) pb->bat->state = CHARGE; else pb->bat->state = NORMAL; return ret; } static struct power_fg power_fg_ops = { .fg_battery_check = power_check_battery, .fg_battery_update = power_update_battery, }; int power_fg_init(unsigned char bus) { static const char name[] = "MAX17042_FG"; struct pmic *p = pmic_alloc(); if (!p) { printf("%s: POWER allocation error!\n", __func__); return -ENOMEM; } debug("Board Fuel Gauge init\n"); p->name = name; p->interface = PMIC_I2C; p->number_of_regs = FG_NUM_OF_REGS; p->hw.i2c.addr = MAX17042_I2C_ADDR; p->hw.i2c.tx_num = 2; p->sensor_byte_order = PMIC_SENSOR_BYTE_ORDER_BIG; p->bus = bus; p->fg = &power_fg_ops; return 0; }