1 /* 2 * Copyright (C) 2012 Samsung Electronics 3 * Lukasz Majewski <l.majewski@samsung.com> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <power/pmic.h> 10 #include <power/max17042_fg.h> 11 #include <i2c.h> 12 #include <power/max8997_pmic.h> 13 #include <power/power_chrg.h> 14 #include <power/battery.h> 15 #include <power/fg_battery_cell_params.h> 16 #include <errno.h> 17 18 static int fg_write_regs(struct pmic *p, u8 addr, u16 *data, int num) 19 { 20 int ret = 0; 21 int i; 22 23 for (i = 0; i < num; i++, addr++) 24 ret |= pmic_reg_write(p, addr, *(data + i)); 25 26 return ret; 27 } 28 29 static int fg_read_regs(struct pmic *p, u8 addr, u16 *data, int num) 30 { 31 int ret = 0; 32 int i; 33 34 for (i = 0; i < num; i++, addr++) 35 ret |= pmic_reg_read(p, addr, (u32 *) (data + i)); 36 37 return ret; 38 } 39 40 static int fg_write_and_verify(struct pmic *p, u8 addr, u16 data) 41 { 42 unsigned int val = data; 43 int ret = 0; 44 45 ret |= pmic_reg_write(p, addr, val); 46 ret |= pmic_reg_read(p, addr, &val); 47 48 if (ret) 49 return ret; 50 51 if (((u16) val) == data) 52 return 0; 53 54 return -1; 55 } 56 57 static void por_fuelgauge_init(struct pmic *p) 58 { 59 u16 r_data0[16], r_data1[16], r_data2[16]; 60 u32 rewrite_count = 5, i = 0; 61 unsigned int val; 62 int ret = 0; 63 64 /* Delay 500 ms */ 65 mdelay(500); 66 /* Initilize Configuration */ 67 pmic_reg_write(p, MAX17042_CONFIG, 0x2310); 68 69 rewrite_model: 70 /* Unlock Model Access */ 71 pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_UNLOCK1); 72 pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_UNLOCK2); 73 74 /* Write/Read/Verify the Custom Model */ 75 ret |= fg_write_regs(p, MAX17042_MODEL1, cell_character0, 76 ARRAY_SIZE(cell_character0)); 77 ret |= fg_write_regs(p, MAX17042_MODEL2, cell_character1, 78 ARRAY_SIZE(cell_character1)); 79 ret |= fg_write_regs(p, MAX17042_MODEL3, cell_character2, 80 ARRAY_SIZE(cell_character2)); 81 82 if (ret) { 83 printf("%s: Cell parameters write failed!\n", __func__); 84 return; 85 } 86 87 ret |= fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); 88 ret |= fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); 89 ret |= fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); 90 91 if (ret) 92 printf("%s: Cell parameters read failed!\n", __func__); 93 94 for (i = 0; i < 16; i++) { 95 if ((cell_character0[i] != r_data0[i]) 96 || (cell_character1[i] != r_data1[i]) 97 || (cell_character2[i] != r_data2[i])) 98 goto rewrite_model; 99 } 100 101 /* Lock model access */ 102 pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_LOCK1); 103 pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_LOCK2); 104 105 /* Verify the model access is locked */ 106 ret |= fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); 107 ret |= fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); 108 ret |= fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); 109 110 if (ret) { 111 printf("%s: Cell parameters read failed!\n", __func__); 112 return; 113 } 114 115 for (i = 0; i < ARRAY_SIZE(r_data0); i++) { 116 /* Check if model locked */ 117 if (r_data0[i] || r_data1[i] || r_data2[i]) { 118 /* Rewrite model data - prevent from endless loop */ 119 if (rewrite_count--) { 120 puts("FG - Lock model access failed!\n"); 121 goto rewrite_model; 122 } 123 } 124 } 125 126 /* Write Custom Parameters */ 127 fg_write_and_verify(p, MAX17042_RCOMP0, RCOMP0); 128 fg_write_and_verify(p, MAX17042_TEMPCO, TempCo); 129 130 /* Delay at least 350mS */ 131 mdelay(350); 132 133 /* Initialization Complete */ 134 pmic_reg_read(p, MAX17042_STATUS, &val); 135 /* Write and Verify Status with POR bit Cleared */ 136 fg_write_and_verify(p, MAX17042_STATUS, val & ~MAX17042_POR); 137 138 /* Delay at least 350 ms */ 139 mdelay(350); 140 } 141 142 static int power_update_battery(struct pmic *p, struct pmic *bat) 143 { 144 struct power_battery *pb = bat->pbat; 145 unsigned int val; 146 int ret = 0; 147 148 if (pmic_probe(p)) { 149 puts("Can't find max17042 fuel gauge\n"); 150 return -1; 151 } 152 153 ret |= pmic_reg_read(p, MAX17042_VFSOC, &val); 154 pb->bat->state_of_chrg = (val >> 8); 155 156 pmic_reg_read(p, MAX17042_VCELL, &val); 157 debug("vfsoc: 0x%x\n", val); 158 pb->bat->voltage_uV = ((val & 0xFFUL) >> 3) + ((val & 0xFF00) >> 3); 159 pb->bat->voltage_uV = (pb->bat->voltage_uV * 625); 160 161 pmic_reg_read(p, 0x05, &val); 162 pb->bat->capacity = val >> 2; 163 164 return ret; 165 } 166 167 static int power_check_battery(struct pmic *p, struct pmic *bat) 168 { 169 struct power_battery *pb = bat->pbat; 170 unsigned int val; 171 int ret = 0; 172 173 if (pmic_probe(p)) { 174 puts("Can't find max17042 fuel gauge\n"); 175 return -1; 176 } 177 178 ret |= pmic_reg_read(p, MAX17042_STATUS, &val); 179 debug("fg status: 0x%x\n", val); 180 181 if (val == MAX17042_POR) 182 por_fuelgauge_init(p); 183 184 ret |= pmic_reg_read(p, MAX17042_VERSION, &val); 185 pb->bat->version = val; 186 187 power_update_battery(p, bat); 188 debug("fg ver: 0x%x\n", pb->bat->version); 189 printf("BAT: state_of_charge(SOC):%d%%\n", 190 pb->bat->state_of_chrg); 191 192 printf(" voltage: %d.%6.6d [V] (expected to be %d [mAh])\n", 193 pb->bat->voltage_uV / 1000000, 194 pb->bat->voltage_uV % 1000000, 195 pb->bat->capacity); 196 197 if (pb->bat->voltage_uV > 3850000) 198 pb->bat->state = EXT_SOURCE; 199 else if (pb->bat->voltage_uV < 3600000 || pb->bat->state_of_chrg < 5) 200 pb->bat->state = CHARGE; 201 else 202 pb->bat->state = NORMAL; 203 204 return ret; 205 } 206 207 static struct power_fg power_fg_ops = { 208 .fg_battery_check = power_check_battery, 209 .fg_battery_update = power_update_battery, 210 }; 211 212 int power_fg_init(unsigned char bus) 213 { 214 static const char name[] = "MAX17042_FG"; 215 struct pmic *p = pmic_alloc(); 216 217 if (!p) { 218 printf("%s: POWER allocation error!\n", __func__); 219 return -ENOMEM; 220 } 221 222 debug("Board Fuel Gauge init\n"); 223 224 p->name = name; 225 p->interface = PMIC_I2C; 226 p->number_of_regs = FG_NUM_OF_REGS; 227 p->hw.i2c.addr = MAX17042_I2C_ADDR; 228 p->hw.i2c.tx_num = 2; 229 p->sensor_byte_order = PMIC_SENSOR_BYTE_ORDER_BIG; 230 p->bus = bus; 231 232 p->fg = &power_fg_ops; 233 return 0; 234 } 235