741 lines
17 KiB
C
741 lines
17 KiB
C
/*
|
|
* smb347_charger.c
|
|
* Samsung SMB347 Charger Driver
|
|
*
|
|
* Copyright (C) 2012 Samsung Electronics
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#define DEBUG
|
|
|
|
#include <linux/battery/sec_charger.h>
|
|
static int smb347_i2c_write(struct i2c_client *client,
|
|
int reg, u8 *buf)
|
|
{
|
|
int ret;
|
|
ret = i2c_smbus_write_i2c_block_data(client, reg, 1, buf);
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "%s: Error(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static int smb347_i2c_read(struct i2c_client *client,
|
|
int reg, u8 *buf)
|
|
{
|
|
int ret;
|
|
ret = i2c_smbus_read_i2c_block_data(client, reg, 1, buf);
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "%s: Error(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void smb347_i2c_write_array(struct i2c_client *client,
|
|
u8 *buf, int size)
|
|
{
|
|
int i;
|
|
for (i = 0; i < size; i += 3)
|
|
smb347_i2c_write(client, (u8) (*(buf + i)), (buf + i) + 1);
|
|
}
|
|
|
|
static void smb347_set_command(struct i2c_client *client,
|
|
int reg, int datum)
|
|
{
|
|
int val;
|
|
u8 data = 0;
|
|
val = smb347_i2c_read(client, reg, &data);
|
|
if (val >= 0) {
|
|
dev_dbg(&client->dev, "%s : reg(0x%02x): 0x%02x",
|
|
__func__, reg, data);
|
|
if (data != datum) {
|
|
data = datum;
|
|
if (smb347_i2c_write(client, reg, &data) < 0)
|
|
dev_err(&client->dev,
|
|
"%s : error!\n", __func__);
|
|
val = smb347_i2c_read(client, reg, &data);
|
|
if (val >= 0)
|
|
dev_dbg(&client->dev, " => 0x%02x\n", data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void smb347_test_read(struct i2c_client *client)
|
|
{
|
|
u8 data = 0;
|
|
u32 addr = 0;
|
|
for (addr = 0; addr <= 0x0f; addr++) {
|
|
smb347_i2c_read(client, addr, &data);
|
|
dev_dbg(&client->dev,
|
|
"smb347 addr : 0x%02x data : 0x%02x\n", addr, data);
|
|
}
|
|
for (addr = 0x30; addr <= 0x3f; addr++) {
|
|
smb347_i2c_read(client, addr, &data);
|
|
dev_dbg(&client->dev,
|
|
"smb347 addr : 0x%02x data : 0x%02x\n", addr, data);
|
|
}
|
|
}
|
|
|
|
static void smb347_read_regs(struct i2c_client *client, char *str)
|
|
{
|
|
u8 data = 0;
|
|
u32 addr = 0;
|
|
|
|
for (addr = 0; addr <= 0x0f; addr++) {
|
|
smb347_i2c_read(client, addr, &data);
|
|
sprintf(str+strlen(str), "0x%x, ", data);
|
|
}
|
|
|
|
/* "#" considered as new line in application */
|
|
sprintf(str+strlen(str), "#");
|
|
|
|
for (addr = 0x30; addr <= 0x3f; addr++) {
|
|
smb347_i2c_read(client, addr, &data);
|
|
sprintf(str+strlen(str), "0x%x, ", data);
|
|
}
|
|
}
|
|
|
|
static int smb347_read_reg(struct i2c_client *client, int reg)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_smbus_read_byte_data(client, reg);
|
|
|
|
if (ret < 0) {
|
|
pr_err("%s: err %d, try again!\n", __func__, ret);
|
|
ret = i2c_smbus_read_byte_data(client, reg);
|
|
if (ret < 0)
|
|
pr_err("%s: err %d\n", __func__, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int smb347_get_charging_status(struct i2c_client *client)
|
|
{
|
|
int status = POWER_SUPPLY_STATUS_UNKNOWN;
|
|
u8 data_a = 0;
|
|
u8 data_b = 0;
|
|
u8 data_c = 0;
|
|
u8 data_d = 0;
|
|
u8 data_e = 0;
|
|
|
|
smb347_i2c_read(client, SMB347_STATUS_A, &data_a);
|
|
dev_info(&client->dev,
|
|
"%s : charger status A(0x%02x)\n", __func__, data_a);
|
|
smb347_i2c_read(client, SMB347_STATUS_B, &data_b);
|
|
dev_info(&client->dev,
|
|
"%s : charger status B(0x%02x)\n", __func__, data_b);
|
|
smb347_i2c_read(client, SMB347_STATUS_C, &data_c);
|
|
dev_info(&client->dev,
|
|
"%s : charger status C(0x%02x)\n", __func__, data_c);
|
|
smb347_i2c_read(client, SMB347_STATUS_D, &data_d);
|
|
dev_info(&client->dev,
|
|
"%s : charger status D(0x%02x)\n", __func__, data_d);
|
|
smb347_i2c_read(client, SMB347_STATUS_E, &data_e);
|
|
dev_info(&client->dev,
|
|
"%s : charger status E(0x%02x)\n", __func__, data_e);
|
|
|
|
/* At least one charge cycle terminated,
|
|
* Charge current < Termination Current
|
|
*/
|
|
if ((data_c & 0x20) == 0x20) {
|
|
/* top-off by full charging */
|
|
status = POWER_SUPPLY_STATUS_FULL;
|
|
goto charging_status_end;
|
|
}
|
|
|
|
/* Is enabled ? */
|
|
if (data_c & 0x01) {
|
|
/* check for 0x06 : no charging (0b00) */
|
|
/* not charging */
|
|
if (!(data_c & 0x06)) {
|
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
goto charging_status_end;
|
|
} else {
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
goto charging_status_end;
|
|
}
|
|
} else
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
charging_status_end:
|
|
return (int)status;
|
|
}
|
|
|
|
static int smb347_get_charging_health(struct i2c_client *client)
|
|
{
|
|
int health = POWER_SUPPLY_HEALTH_GOOD;
|
|
u8 data_a = 0;
|
|
u8 data_b = 0;
|
|
u8 data_c = 0;
|
|
u8 data_d = 0;
|
|
u8 data_e = 0;
|
|
|
|
smb347_i2c_read(client, SMB347_STATUS_A, &data_a);
|
|
dev_info(&client->dev,
|
|
"%s : charger status A(0x%02x)\n", __func__, data_a);
|
|
smb347_i2c_read(client, SMB347_STATUS_B, &data_b);
|
|
dev_info(&client->dev,
|
|
"%s : charger status B(0x%02x)\n", __func__, data_b);
|
|
smb347_i2c_read(client, SMB347_STATUS_C, &data_c);
|
|
dev_info(&client->dev,
|
|
"%s : charger status C(0x%02x)\n", __func__, data_c);
|
|
smb347_i2c_read(client, SMB347_STATUS_D, &data_d);
|
|
dev_info(&client->dev,
|
|
"%s : charger status D(0x%02x)\n", __func__, data_d);
|
|
smb347_i2c_read(client, SMB347_STATUS_E, &data_e);
|
|
dev_info(&client->dev,
|
|
"%s : charger status E(0x%02x)\n", __func__, data_e);
|
|
|
|
/* Is enabled ? */
|
|
if (data_c & 0x01) {
|
|
if (!(data_a & 0x02)) /* Input current is NOT OK */
|
|
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
|
}
|
|
return (int)health;
|
|
}
|
|
|
|
static void smb347_allow_volatile_writes(struct i2c_client *client)
|
|
{
|
|
int val, reg;
|
|
u8 data;
|
|
reg = SMB347_COMMAND_A;
|
|
val = smb347_i2c_read(client, reg, &data);
|
|
if ((val >= 0) && !(data & 0x80)) {
|
|
dev_dbg(&client->dev,
|
|
"%s : reg(0x%02x): 0x%02x", __func__, reg, data);
|
|
data |= (0x1 << 7);
|
|
if (smb347_i2c_write(client, reg, &data) < 0)
|
|
dev_err(&client->dev, "%s : error!\n", __func__);
|
|
val = smb347_i2c_read(client, reg, &data);
|
|
if (val >= 0) {
|
|
data = (u8) data;
|
|
dev_dbg(&client->dev, " => 0x%02x\n", data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static u8 smb347_get_float_voltage_data(
|
|
int float_voltage)
|
|
{
|
|
u8 data;
|
|
|
|
if (float_voltage < 3500)
|
|
float_voltage = 3500;
|
|
|
|
data = (float_voltage - 3500) / 20;
|
|
|
|
return data;
|
|
}
|
|
|
|
static u8 smb347_get_input_current_limit_data(
|
|
struct sec_charger_info *charger, int input_current)
|
|
{
|
|
u8 data;
|
|
|
|
if (input_current <= 300)
|
|
data = 0x0;
|
|
else if (input_current <= 500)
|
|
data = 0x1;
|
|
else if (input_current <= 700)
|
|
data = 0x2;
|
|
else if (input_current <= 900)
|
|
data = 0x3;
|
|
else if (input_current <= 1200)
|
|
data = 0x4;
|
|
else if (input_current <= 1500)
|
|
data = 0x5;
|
|
else if (input_current <= 1800)
|
|
data = 0x6;
|
|
else if (input_current <= 2000)
|
|
data = 0x7;
|
|
else if (input_current <= 2200)
|
|
data = 0x8;
|
|
else if (input_current <= 2500)
|
|
data = 0x9;
|
|
else
|
|
data = 0;
|
|
|
|
return data;
|
|
}
|
|
|
|
static u8 smb347_get_termination_current_limit_data(
|
|
int termination_current)
|
|
{
|
|
u8 data;
|
|
|
|
if (termination_current <= 37)
|
|
data = 0x0;
|
|
else if (termination_current <= 50)
|
|
data = 0x1;
|
|
else if (termination_current <= 100)
|
|
data = 0x2;
|
|
else if (termination_current <= 150)
|
|
data = 0x3;
|
|
else if (termination_current <= 200)
|
|
data = 0x4;
|
|
else if (termination_current <= 250)
|
|
data = 0x5;
|
|
else if (termination_current <= 500)
|
|
data = 0x6;
|
|
else if (termination_current <= 600)
|
|
data = 0x7;
|
|
else
|
|
data = 0;
|
|
|
|
return data;
|
|
}
|
|
|
|
static u8 smb347_get_fast_charging_current_data(
|
|
int fast_charging_current)
|
|
{
|
|
u8 data;
|
|
|
|
if (fast_charging_current <= 700)
|
|
data = 0x0;
|
|
else if (fast_charging_current <= 900)
|
|
data = 0x1;
|
|
else if (fast_charging_current <= 1200)
|
|
data = 0x2;
|
|
else if (fast_charging_current <= 1500)
|
|
data = 0x3;
|
|
else if (fast_charging_current <= 1800)
|
|
data = 0x4;
|
|
else if (fast_charging_current <= 2000)
|
|
data = 0x5;
|
|
else if (fast_charging_current <= 2200)
|
|
data = 0x6;
|
|
else if (fast_charging_current <= 2500)
|
|
data = 0x7;
|
|
else
|
|
data = 0;
|
|
|
|
return data << 5;
|
|
}
|
|
|
|
static void smb347_charger_function_conrol(
|
|
struct i2c_client *client)
|
|
{
|
|
struct sec_charger_info *charger = i2c_get_clientdata(client);
|
|
u8 data;
|
|
|
|
if (charger->charging_current < 0) {
|
|
dev_dbg(&client->dev,
|
|
"%s : OTG is activated. Ignore command!\n", __func__);
|
|
return;
|
|
}
|
|
smb347_allow_volatile_writes(client);
|
|
|
|
if (charger->cable_type ==
|
|
POWER_SUPPLY_TYPE_BATTERY) {
|
|
/* turn off charger */
|
|
smb347_set_command(client,
|
|
SMB347_COMMAND_A, 0x80);
|
|
|
|
/* high current mode for system current */
|
|
smb347_set_command(client,
|
|
SMB347_COMMAND_B, 0x01);
|
|
} else {
|
|
/* Pre-charge curr 250mA */
|
|
dev_dbg(&client->dev,
|
|
"%s : fast charging current (%dmA)\n",
|
|
__func__, charger->charging_current);
|
|
dev_dbg(&client->dev,
|
|
"%s : termination current (%dmA)\n",
|
|
__func__, charger->pdata->charging_current[
|
|
charger->cable_type].full_check_current_1st);
|
|
data = 0x1c;
|
|
data |= smb347_get_fast_charging_current_data(
|
|
charger->charging_current);
|
|
data |= smb347_get_termination_current_limit_data(
|
|
charger->pdata->charging_current[
|
|
charger->cable_type].full_check_current_1st);
|
|
smb347_set_command(client,
|
|
SMB347_CHARGE_CURRENT, data);
|
|
|
|
/* Pin enable control */
|
|
/* DCIN Input Pre-bias Enable */
|
|
data = 0x01;
|
|
if (charger->pdata->chg_gpio_en)
|
|
data |= 0x40;
|
|
if (charger->pdata->chg_polarity_en)
|
|
data |= 0x20;
|
|
smb347_set_command(client,
|
|
SMB347_PIN_ENABLE_CONTROL, data);
|
|
|
|
/* Input current limit */
|
|
dev_dbg(&client->dev, "%s : input current (%dmA)\n",
|
|
__func__, charger->pdata->charging_current
|
|
[charger->cable_type].input_current_limit);
|
|
data = 0;
|
|
data = smb347_get_input_current_limit_data(
|
|
charger,
|
|
charger->pdata->charging_current
|
|
[charger->cable_type].input_current_limit);
|
|
smb347_set_command(client,
|
|
SMB347_INPUT_CURRENTLIMIT, data);
|
|
|
|
/*
|
|
* Input to System FET by Register
|
|
* Enable AICL, VCHG
|
|
* Max System voltage =Vflt + 0.1v
|
|
* Input Source Priority : USBIN
|
|
*/
|
|
if (charger->pdata->chg_functions_setting &
|
|
SEC_CHARGER_NO_GRADUAL_CHARGING_CURRENT)
|
|
/* disable AICL */
|
|
smb347_set_command(client,
|
|
SMB347_VARIOUS_FUNCTIONS, 0x85);
|
|
else
|
|
/* enable AICL */
|
|
smb347_set_command(client,
|
|
SMB347_VARIOUS_FUNCTIONS, 0x95);
|
|
|
|
/* Float voltage, Vprechg : 2.4V */
|
|
dev_dbg(&client->dev, "%s : float voltage (%dmV)\n",
|
|
__func__, charger->pdata->chg_float_voltage);
|
|
data = 0;
|
|
data |= smb347_get_float_voltage_data(
|
|
charger->pdata->chg_float_voltage);
|
|
smb347_set_command(client,
|
|
SMB347_FLOAT_VOLTAGE, data);
|
|
|
|
/* Charge control
|
|
* Automatic Recharge disable,
|
|
* Current Termination disable,
|
|
* BMD disable, Recharge Threshold =50mV,
|
|
* APSD disable */
|
|
data = 0xC0;
|
|
switch (charger->pdata->full_check_type) {
|
|
case SEC_BATTERY_FULLCHARGED_CHGGPIO:
|
|
case SEC_BATTERY_FULLCHARGED_CHGINT:
|
|
case SEC_BATTERY_FULLCHARGED_CHGPSY:
|
|
/* Enable Current Termination */
|
|
data &= 0xBF;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
smb347_set_command(client,
|
|
SMB347_CHARGE_CONTROL, data);
|
|
|
|
/* STAT, Timer control : STAT active low,
|
|
* Complete time out 1527min.
|
|
*/
|
|
smb347_set_command(client,
|
|
SMB347_STAT_TIMERS_CONTROL, 0x1A);
|
|
|
|
/* Pin/Enable
|
|
* USB 5/1/HC Dual state
|
|
* DCIN pre-bias Enable
|
|
*/
|
|
smb347_set_command(client,
|
|
SMB347_PIN_ENABLE_CONTROL, 0x09);
|
|
|
|
/* Therm control :
|
|
* Therm monitor disable,
|
|
* Minimum System Voltage 3.60V
|
|
*/
|
|
smb347_set_command(client,
|
|
SMB347_THERM_CONTROL_A, 0x7F);
|
|
|
|
/* USB selection : USB2.0(100mA/500mA),
|
|
* INOK polarity Active low
|
|
*/
|
|
smb347_set_command(client,
|
|
SMB347_SYSOK_USB30_SELECTION, 0x08);
|
|
|
|
/* Other control
|
|
* Low batt detection disable
|
|
* Minimum System Voltage 3.60V
|
|
*/
|
|
smb347_set_command(client,
|
|
SMB347_OTHER_CONTROL_A, 0x00);
|
|
|
|
/* OTG tlim therm control */
|
|
smb347_set_command(client,
|
|
SMB347_OTG_TLIM_THERM_CONTROL, 0x3F);
|
|
|
|
/* Limit cell temperature */
|
|
smb347_set_command(client,
|
|
SMB347_LIMIT_CELL_TEMPERATURE_MONITOR, 0x01);
|
|
|
|
/* Fault interrupt : Clear */
|
|
smb347_set_command(client,
|
|
SMB347_FAULT_INTERRUPT, 0x00);
|
|
|
|
/* STATUS ingerrupt : Clear */
|
|
smb347_set_command(client,
|
|
SMB347_STATUS_INTERRUPT, 0x00);
|
|
|
|
/* turn on charger */
|
|
smb347_set_command(client,
|
|
SMB347_COMMAND_A, 0xC2);
|
|
|
|
/* HC or USB5 mode */
|
|
switch (charger->cable_type) {
|
|
case POWER_SUPPLY_TYPE_MAINS:
|
|
case POWER_SUPPLY_TYPE_MISC:
|
|
/* High-current mode */
|
|
data = 0x01;
|
|
break;
|
|
case POWER_SUPPLY_TYPE_USB:
|
|
case POWER_SUPPLY_TYPE_USB_DCP:
|
|
case POWER_SUPPLY_TYPE_USB_CDP:
|
|
case POWER_SUPPLY_TYPE_USB_ACA:
|
|
/* USB5 */
|
|
data = 0x02;
|
|
break;
|
|
default:
|
|
/* USB1 */
|
|
data = 0x00;
|
|
break;
|
|
}
|
|
smb347_set_command(client,
|
|
SMB347_COMMAND_B, data);
|
|
}
|
|
}
|
|
|
|
static void smb347_charger_otg_conrol(
|
|
struct i2c_client *client)
|
|
{
|
|
struct sec_charger_info *charger = i2c_get_clientdata(client);
|
|
smb347_allow_volatile_writes(client);
|
|
if (charger->cable_type ==
|
|
POWER_SUPPLY_TYPE_BATTERY) {
|
|
/* turn off charger */
|
|
smb347_set_command(client,
|
|
SMB347_COMMAND_A, 0x80);
|
|
} else {
|
|
/* turn on OTG */
|
|
smb347_set_command(client,
|
|
SMB347_COMMAND_A, (0x1 << 4));
|
|
}
|
|
}
|
|
|
|
static int smb347_check_charging_status(struct i2c_client *client)
|
|
{
|
|
int val, reg;
|
|
u8 data = 0;
|
|
int ret = -1;
|
|
|
|
reg = SMB347_STATUS_C; /* SMB328A_BATTERY_CHARGING_STATUS_C */
|
|
val = smb347_read_reg(client, reg);
|
|
if (val >= 0) {
|
|
data = (u8) val;
|
|
pr_debug("%s : reg (0x%x) = 0x%x\n", __func__, reg, data);
|
|
|
|
ret = (data & (0x3 << 1)) >> 1;
|
|
pr_debug("%s : status = 0x%x\n", __func__, data);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool sec_hal_chg_init(struct i2c_client *client)
|
|
{
|
|
smb347_test_read(client);
|
|
return true;
|
|
}
|
|
|
|
bool sec_hal_chg_suspend(struct i2c_client *client)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool sec_hal_chg_resume(struct i2c_client *client)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool sec_hal_chg_get_property(struct i2c_client *client,
|
|
enum power_supply_property psp,
|
|
union power_supply_propval *val)
|
|
{
|
|
struct sec_charger_info *charger = i2c_get_clientdata(client);
|
|
u8 data;
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
val->intval = smb347_get_charging_status(client);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
switch (smb347_check_charging_status(client)) {
|
|
case 0:
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
|
break;
|
|
case 1:
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
|
|
break;
|
|
case 2:
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
|
break;
|
|
case 3:
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
|
break;
|
|
default:
|
|
pr_err("%s : get charge type error!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
val->intval = smb347_get_charging_health(client);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
if (charger->charging_current) {
|
|
smb347_i2c_read(client, SMB347_STATUS_B, &data);
|
|
if (data & 0x20)
|
|
switch (data & 0x18) {
|
|
case 0:
|
|
val->intval = 100;
|
|
break;
|
|
case 1:
|
|
val->intval = 150;
|
|
break;
|
|
case 2:
|
|
val->intval = 200;
|
|
break;
|
|
case 3:
|
|
val->intval = 250;
|
|
break;
|
|
}
|
|
else
|
|
switch (data & 0x07) {
|
|
case 0:
|
|
val->intval = 700;
|
|
break;
|
|
case 1:
|
|
val->intval = 900;
|
|
break;
|
|
case 2:
|
|
val->intval = 1200;
|
|
break;
|
|
case 3:
|
|
val->intval = 1500;
|
|
break;
|
|
case 4:
|
|
val->intval = 1800;
|
|
break;
|
|
case 5:
|
|
val->intval = 2000;
|
|
break;
|
|
case 6:
|
|
val->intval = 2200;
|
|
break;
|
|
case 7:
|
|
val->intval = 2500;
|
|
break;
|
|
}
|
|
} else
|
|
val->intval = 0;
|
|
dev_dbg(&client->dev,
|
|
"%s : set-current(%dmA), current now(%dmA)\n",
|
|
__func__, charger->charging_current, val->intval);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool sec_hal_chg_set_property(struct i2c_client *client,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct sec_charger_info *charger = i2c_get_clientdata(client);
|
|
|
|
switch (psp) {
|
|
/* val->intval : type */
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
/* val->intval : charging current */
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
if (charger->charging_current < 0)
|
|
smb347_charger_otg_conrol(client);
|
|
else if (charger->charging_current > 0)
|
|
smb347_charger_function_conrol(client);
|
|
else {
|
|
smb347_charger_function_conrol(client);
|
|
smb347_charger_otg_conrol(client);
|
|
}
|
|
smb347_test_read(client);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ssize_t sec_hal_chg_show_attrs(struct device *dev,
|
|
const ptrdiff_t offset, char *buf)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct sec_charger_info *chg =
|
|
container_of(psy, struct sec_charger_info, psy_chg);
|
|
int i = 0;
|
|
char *str = NULL;
|
|
|
|
switch (offset) {
|
|
case CHG_DATA:
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n",
|
|
chg->reg_data);
|
|
break;
|
|
case CHG_REGS:
|
|
str = kzalloc(sizeof(char)*1024, GFP_KERNEL);
|
|
if (!str)
|
|
return -ENOMEM;
|
|
|
|
smb347_read_regs(chg->client, str);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n",
|
|
str);
|
|
|
|
kfree(str);
|
|
break;
|
|
default:
|
|
i = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
ssize_t sec_hal_chg_store_attrs(struct device *dev,
|
|
const ptrdiff_t offset,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct sec_charger_info *chg =
|
|
container_of(psy, struct sec_charger_info, psy_chg);
|
|
int ret = 0;
|
|
int x = 0;
|
|
u8 data = 0;
|
|
|
|
switch (offset) {
|
|
case CHG_REG:
|
|
if (sscanf(buf, "%x\n", &x) == 1) {
|
|
chg->reg_addr = x;
|
|
smb347_i2c_read(chg->client,
|
|
chg->reg_addr, &data);
|
|
chg->reg_data = data;
|
|
dev_dbg(dev, "%s: (read) addr = 0x%x, data = 0x%x\n",
|
|
__func__, chg->reg_addr, chg->reg_data);
|
|
ret = count;
|
|
}
|
|
break;
|
|
case CHG_DATA:
|
|
if (sscanf(buf, "%x\n", &x) == 1) {
|
|
data = (u8)x;
|
|
dev_dbg(dev, "%s: (write) addr = 0x%x, data = 0x%x\n",
|
|
__func__, chg->reg_addr, data);
|
|
smb347_i2c_write(chg->client,
|
|
chg->reg_addr, &data);
|
|
ret = count;
|
|
}
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|