diff options
-rw-r--r-- | drivers/power/smb349-charger.c | 221 | ||||
-rw-r--r-- | include/linux/smb349-charger.h | 27 |
2 files changed, 195 insertions, 53 deletions
diff --git a/drivers/power/smb349-charger.c b/drivers/power/smb349-charger.c index b3db989fa070..6a4c738b4647 100644 --- a/drivers/power/smb349-charger.c +++ b/drivers/power/smb349-charger.c @@ -32,6 +32,9 @@ #include <linux/regulator/machine.h> #include <linux/smb349-charger.h> #include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #define SMB349_CHARGE 0x00 #define SMB349_CHRG_CRNTS 0x01 @@ -68,11 +71,14 @@ #define SMB349_STS_REG_D 0x3E #define SMB349_STS_REG_E 0x3F +#define SMB349_ENABLE_WRITE 1 +#define SMB349_DISABLE_WRITE 0 #define ENABLE_WRT_ACCESS 0x80 #define THERM_CTRL 0x10 #define BATTERY_MISSING 0x10 #define CHARGING 0x06 #define DEDICATED_CHARGER 0x04 +#define CHRG_DOWNSTRM_PORT 0x08 #define ENABLE_CHARGE 0x02 static struct smb349_charger *charger; @@ -120,21 +126,145 @@ static int smb349_update_reg(struct i2c_client *client, int reg, u8 value) return ret; } -int smb349_volatile_writes(struct i2c_client *client) +int smb349_volatile_writes(struct i2c_client *client, uint8_t value) { int ret = 0; - /* Enable volatile write to config registers*/ - ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_WRT_ACCESS); + if (value == SMB349_ENABLE_WRITE) { + /* Enable volatile write to config registers */ + ret = smb349_update_reg(client, SMB349_CMD_REG, + ENABLE_WRT_ACCESS); + if (ret < 0) { + dev_err(&client->dev, "%s(): Failed in writing" + "register 0x%02x\n", __func__, SMB349_CMD_REG); + return ret; + } + } else { + ret = smb349_read(client, SMB349_CMD_REG); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + + ret = smb349_write(client, SMB349_CMD_REG, ret & (~(1<<7))); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + } + return ret; +} + +static void smb349_clear_interrupts(struct i2c_client *client) +{ + uint8_t val, buf[6]; + + val = i2c_smbus_read_i2c_block_data(client, SMB349_INTR_STS_A, 6, buf); + if (val < 0) + dev_err(&client->dev, "%s(): Failed in clearing interrupts\n", + __func__); +} + +static int smb349_configure_charger(struct i2c_client *client) +{ + int ret = 0; + + /* Enable volatile writes to registers */ + ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring charger..\n", + __func__); + goto error; + } + + /* Enable charging */ + ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_CHARGE); if (ret < 0) { dev_err(&client->dev, "%s(): Failed in writing register" "0x%02x\n", __func__, SMB349_CMD_REG); - return ret; + goto error; } + /* Configure THERM ctrl */ + ret = smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + goto error; + } + + /* Disable volatile writes to registers */ + ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring charger..\n", + __func__); + goto error; + } +error: return ret; } +static irqreturn_t smb349_status_isr(int irq, void *dev_id) +{ + struct i2c_client *client = charger->client; + int ret, val; + + val = smb349_read(client, SMB349_STS_REG_D); + if (val < 0) { + dev_err(&client->dev, "%s(): Failed in reading register" + "0x%02x\n", __func__, SMB349_STS_REG_D); + goto irq_error; + } else if (val != 0) { + + if (val & DEDICATED_CHARGER) + charger->chrg_type = AC; + else if (val & CHRG_DOWNSTRM_PORT) + charger->chrg_type = USB; + + /* configure charger */ + ret = smb349_configure_charger(client); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring" + "charger..\n", __func__); + goto irq_error; + } + + charger->state = progress; + } else { + charger->state = stopped; + + ret = smb349_read(client, SMB349_CMD_REG); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + goto irq_error; + } + + ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<1)))); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + goto irq_error; + } + } + + if (charger->charger_cb) + charger->charger_cb(charger->state, charger->chrg_type, + charger->charger_cb_data); +irq_error: + smb349_clear_interrupts(client); + return IRQ_HANDLED; +} + +int register_callback(charging_callback_t cb, void *args) +{ + struct smb349_charger *charger_data = charger; + if (!charger_data) + return -ENODEV; + + charger_data->charger_cb = cb; + charger_data->charger_cb_data = args; + return 0; +} +EXPORT_SYMBOL_GPL(register_callback); + int smb349_battery_online(void) { int val; @@ -151,50 +281,49 @@ int smb349_battery_online(void) else return 1; } -EXPORT_SYMBOL_GPL(smb349_battery_online); -int smb349_charging_status(void) +static int smb349_configure_interrupts(struct i2c_client *client) { - int val; - struct i2c_client *client = charger->client; + int ret = 0; - val = smb349_read(charger->client, SMB349_STS_REG_C); - if (val < 0) { - dev_err(&client->dev, "%s(): Failed in reading register" - "0x%02x\n", __func__, SMB349_STS_REG_C); - return val; + /* Enable volatile writes to registers */ + ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring charger..\n", + __func__); + goto error; } - if (val & CHARGING) - return 1; - else - return 0; -} -EXPORT_SYMBOL_GPL(smb349_charging_status); -int smb349_charger_type(void) -{ - int val; - struct i2c_client *client = charger->client; + ret = smb349_update_reg(client, SMB349_FAULT_INTR, 0xff); + if (ret < 0) { + dev_err(&client->dev, "%s(): Failed in writing register" + "0x%02x\n", __func__, SMB349_CMD_REG); + goto error; + } - val = smb349_read(charger->client, SMB349_STS_REG_D); - if (val < 0) { - dev_err(&client->dev, "%s(): Failed in reading register" - "0x%02x\n", __func__, SMB349_STS_REG_D); - return val; + ret = smb349_update_reg(client, SMB349_STS_INTR_1, 0xff); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + goto error; + } + + /* Disable volatile writes to registers */ + ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring charger..\n", + __func__); + goto error; } - if (val & DEDICATED_CHARGER) - return 1; - else - return 0; +error: + return ret; } -EXPORT_SYMBOL_GPL(smb349_charger_type); static int __devinit smb349_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - int ret; + int ret, irq_num; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; @@ -207,33 +336,27 @@ static int __devinit smb349_probe(struct i2c_client *client, charger->dev = &client->dev; i2c_set_clientdata(client, charger); - /* Check battery presence*/ + /* Check battery presence */ if (!smb349_battery_online()) { dev_err(&client->dev, "%s() No Battery present, exiting..\n", __func__); goto error; } - /*Enable volatile writes to registers*/ - ret = smb349_volatile_writes(client); + ret = smb349_configure_interrupts(client); if (ret < 0) { dev_err(&client->dev, "%s() error in configuring charger..\n", __func__); goto error; } - /* Configure THERM ctrl*/ - smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL); - if (ret < 0) { - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - goto error; - } - - /* Enable charging*/ - ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_CHARGE); - if (ret < 0) { - dev_err(&client->dev, "%s(): Failed in writing register" - "0x%02x\n", __func__, SMB349_CMD_REG); + irq_num = gpio_to_irq(client->irq); + ret = request_threaded_irq(irq_num, + NULL, smb349_status_isr, IRQ_TYPE_EDGE_FALLING, + "smb349", charger); + if (ret) { + dev_err(&client->dev, "%s(): Failed in requesting isr\n", + __func__); goto error; } diff --git a/include/linux/smb349-charger.h b/include/linux/smb349-charger.h index 7c48a7370b19..e83a678d9958 100644 --- a/include/linux/smb349-charger.h +++ b/include/linux/smb349-charger.h @@ -25,16 +25,35 @@ #include <linux/regulator/machine.h> +enum charging_states { + idle, + progress, + completed, + stopped, +}; + +enum charger_type { + AC, + USB, +}; + +typedef void (*charging_callback_t)(enum charging_states state, +enum charger_type chrg_type, void *args); + struct smb349_charger { struct i2c_client *client; struct device *dev; + void *charger_cb_data; + enum charging_states state; + enum charger_type chrg_type; + charging_callback_t charger_cb; }; +int smb349_battery_online(void); /* - * Register the callback function for the client. + * Register callback function for the client. + * Used by fuel-gauge driver to get battery charging properties. */ -extern int smb349_battery_online(void); -extern int smb349_charging_status(void); -extern int smb349_charger_type(void); +extern int register_callback(charging_callback_t cb, void *args); #endif /*__LINUX_SMB349_CHARGER_H */ |