int ltc294x_get_current( int *val) { struct ltc294x_info *info = <c2943_info; int ret; u8 datar[2]; s32 value; // reset will have manual trigger control flag sent // ltc294x_reset(2); // for(i=0;i<5;i++) // { // nrf_delay_ms(40); // ret = ltc294x_read_regs(info->client, // LTC294X_REG_CONTROL, &datar[0], 1); // if((datar[0]<C2943_REG_CONTROL_MODE_MASK)==0) // break; // } // if (i>=5) return -1; ret = ltc294x_read_regs(info->client, LTC294X_REG_CURRENT_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; value -= 0x7FFF; /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm, * the formula below keeps everything in s32 range while preserving * enough digits */ *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */ return ret; }
static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) { int ret; u8 value; u8 control; /* Read status and control registers */ ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1); if (ret < 0) { dev_err(&info->client->dev, "Could not read registers from device\n"); goto error_exit; } control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED; /* Put the 2943 into "monitor" mode, so it measures every 10 sec */ if (info->num_regs == LTC2943_NUM_REGS) control |= LTC2943_REG_CONTROL_MODE_SCAN; if (value != control) { ret = ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, &control, 1); if (ret < 0) { dev_err(&info->client->dev, "Could not write register\n"); goto error_exit; } } return 0; error_exit: return ret; }
static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val) { int ret; u8 datar[2]; u32 value; ret = ltc294x_read_regs(info->client, LTC294X_REG_VOLTAGE_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; switch (info->id) { case LTC2943_ID: value *= 23600 * 2; value /= 0xFFFF; value *= 1000 / 2; break; case LTC2944_ID: value *= 70800 / 5*4; value /= 0xFFFF; value *= 1000 * 5/4; break; default: value *= 6000 * 10; value /= 0xFFFF; value *= 1000 / 10; break; } *val = value; return ret; }
int ltc294x_reset( int prescaler_exp) { struct ltc294x_info *info = <c2943_info; int ret; u8 value[3]; u8 control; /* Read status and control registers */ ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value[0], 1); if (ret < 0) { dev_err(&info->client->dev, "Could not read registers from device\n"); goto error_exit; } control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | LTC294X_REG_CONTROL_ALCC_CONFIG_OUTPUT_ALERT; /* Set the 2943 mode*/ if (info->num_regs == LTC2943_NUM_REGS) control |= LTC2943_REG_CONTROL_MODE_SCAN; do { if (value[0] != control) { ret = ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, &control, 1); if (ret < 0) { dev_err(&info->client->dev, "Could not write register\n"); goto error_exit; } } /* Read status and control registers again */ ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value[0], 1); if (ret < 0) { dev_err(&info->client->dev, "Could not read registers from device\n"); goto error_exit; } } while(value[0]!= control); return 0; error_exit: return ret; }
static int ltc294x_read_charge_register(const struct ltc294x_info *info) { int ret; u8 datar[2]; ret = ltc294x_read_regs(info->client, LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2); if (ret < 0) return ret; return (datar[0] << 8) + datar[1]; }
static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val) { int ret; u8 datar[2]; u32 value; ret = ltc294x_read_regs(info->client, LTC294X_REG_VOLTAGE_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */ return ret; }
static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) { int ret; u8 datar[2]; u32 value; ret = ltc294x_read_regs(info->client, LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; /* Full-scale is 510 Kelvin, convert to centidegrees */ *val = (((51000 * value) / 0xFFFF) - 27215); return ret; }
static int ltc294x_get_current(const struct ltc294x_info *info, int *val) { int ret; u8 datar[2]; s32 value; ret = ltc294x_read_regs(info->client, LTC294X_REG_CURRENT_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; value -= 0x7FFF; /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm, * the formula below keeps everything in s32 range while preserving * enough digits */ *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */ return ret; }
static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) { int ret; u8 value; u8 control; /* Read status and control registers */ ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1); if (ret < 0) { dev_err(&info->client->dev, "Could not read registers from device\n"); goto error_exit; } control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED; /* Put device into "monitor" mode */ switch (info->id) { case LTC2942_ID: /* 2942 measures every 2 sec */ control |= LTC2942_REG_CONTROL_MODE_SCAN; break; case LTC2943_ID: case LTC2944_ID: /* 2943 and 2944 measure every 10 sec */ control |= LTC2943_REG_CONTROL_MODE_SCAN; break; default: break; } if (value != control) { ret = ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, &control, 1); if (ret < 0) { dev_err(&info->client->dev, "Could not write register\n"); goto error_exit; } } return 0; error_exit: return ret; }
static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) { enum ltc294x_reg reg; int ret; u8 datar[2]; u32 value; if (info->id == LTC2942_ID) { reg = LTC2942_REG_TEMPERATURE_MSB; value = 60000; /* Full-scale is 600 Kelvin */ } else { reg = LTC2943_REG_TEMPERATURE_MSB; value = 51000; /* Full-scale is 510 Kelvin */ } ret = ltc294x_read_regs(info->client, reg, &datar[0], 2); value *= (datar[0] << 8) | datar[1]; /* Convert to centidegrees */ *val = value / 0xFFFF - 27215; return ret; }
static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val) { int ret; u8 dataw[2]; u8 ctrl_reg; s32 value; value = convert_uAh_to_bin(info, val); /* Direction depends on how sense+/- were connected */ if (info->Qlsb < 0) value += 0xFFFF; if ((value < 0) || (value > 0xFFFF)) /* input validation */ return -EINVAL; /* Read control register */ ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &ctrl_reg, 1); if (ret < 0) return ret; /* Disable analog section */ ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK; ret = ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, &ctrl_reg, 1); if (ret < 0) return ret; /* Set new charge value */ dataw[0] = I16_MSB(value); dataw[1] = I16_LSB(value); ret = ltc294x_write_regs(info->client, LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2); if (ret < 0) goto error_exit; /* Enable analog section */ error_exit: ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK; ret = ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, &ctrl_reg, 1); return ret < 0 ? ret : 0; }
static int ltc294x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ltc294x_platform_data *platform_data; struct ltc294x_info *info; int ret; u32 prescaler_exp; u8 status; info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) return -ENOMEM; i2c_set_clientdata(client, info); platform_data = client->dev.platform_data; info->id = (enum ltc294x_id)id->driver_data; info->supply.name = platform_data->name; /* r_sense can be negative, when sense+ is connected to the battery * instead of the sense-. This results in reversed measurements. */ info->r_sense = platform_data->r_sense; prescaler_exp = platform_data->prescaler_exp; if (info->id == LTC2943_ID) { if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP) prescaler_exp = LTC2943_MAX_PRESCALER_EXP; info->Qlsb = ((340 * 50000) / info->r_sense) / (4096 / (1 << (2*prescaler_exp))); } else { if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP) prescaler_exp = LTC2941_MAX_PRESCALER_EXP; info->Qlsb = ((85 * 50000) / info->r_sense) / (128 / (1 << prescaler_exp)); } /* Read status register to check for LTC2942 */ if (info->id == LTC2941_ID || info->id == LTC2942_ID) { ret = ltc294x_read_regs(client, LTC294X_REG_STATUS, &status, 1); if (ret < 0) { dev_err(&client->dev, "Could not read status register\n"); return ret; } if (status & LTC2941_REG_STATUS_CHIP_ID) info->id = LTC2941_ID; else info->id = LTC2942_ID; } info->client = client; info->supply.type = POWER_SUPPLY_TYPE_BATTERY; info->supply.properties = ltc294x_properties; switch (info->id) { case LTC2944_ID: case LTC2943_ID: info->supply.num_properties = ARRAY_SIZE(ltc294x_properties); break; case LTC2942_ID: info->supply.num_properties = ARRAY_SIZE(ltc294x_properties) - 1; break; case LTC2941_ID: default: info->supply.num_properties = ARRAY_SIZE(ltc294x_properties) - 3; break; } info->supply.get_property = ltc294x_get_property; info->supply.set_property = ltc294x_set_property; info->supply.property_is_writeable = ltc294x_property_is_writeable; info->supply.external_power_changed = NULL; INIT_DELAYED_WORK(&info->work, ltc294x_work); ret = ltc294x_reset(info, prescaler_exp); if (ret < 0) { dev_err(&client->dev, "Communication with chip failed\n"); return ret; } ret = power_supply_register(&client->dev, &info->supply); if (ret) { dev_err(&client->dev, "failed to register ltc2941\n"); return ret; } else { schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); } return 0; }