| /* Copyright (c) 2012 The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| #define pr_fmt(fmt) "%s: " fmt, __func__ |
| |
| #include <linux/i2c.h> |
| #include <linux/gpio.h> |
| #include <linux/errno.h> |
| #include <linux/delay.h> |
| #include <linux/module.h> |
| #include <linux/debugfs.h> |
| #include <linux/workqueue.h> |
| #include <linux/interrupt.h> |
| #include <linux/slab.h> |
| #include <linux/i2c/smb349.h> |
| #include <linux/power_supply.h> |
| |
| #define SMB349_MASK(BITS, POS) ((unsigned char)(((1 << BITS) - 1) << POS)) |
| |
| /* Register definitions */ |
| #define CHG_CURRENT_REG 0x00 |
| #define CHG_OTHER_CURRENT_REG 0x01 |
| #define VAR_FUNC_REG 0x02 |
| #define FLOAT_VOLTAGE_REG 0x03 |
| #define CHG_CTRL_REG 0x04 |
| #define STAT_TIMER_REG 0x05 |
| #define PIN_ENABLE_CTRL_REG 0x06 |
| #define THERM_CTRL_A_REG 0x07 |
| #define SYSOK_USB3_SELECT_REG 0x08 |
| #define CTRL_FUNCTIONS_REG 0x09 |
| #define OTG_TLIM_THERM_CNTRL_REG 0x0A |
| #define HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG 0x0B |
| #define FAULT_IRQ_REG 0x0C |
| #define STATUS_IRQ_REG 0x0D |
| #define SYSOK_REG 0x0E |
| #define CMD_A_REG 0x30 |
| #define CMD_B_REG 0x31 |
| #define CMD_C_REG 0x33 |
| #define IRQ_A_REG 0x35 |
| #define IRQ_B_REG 0x36 |
| #define IRQ_C_REG 0x37 |
| #define IRQ_D_REG 0x38 |
| #define IRQ_E_REG 0x39 |
| #define IRQ_F_REG 0x3A |
| #define STATUS_A_REG 0x3B |
| #define STATUS_B_REG 0x3C |
| #define STATUS_C_REG 0x3D |
| #define STATUS_D_REG 0x3E |
| #define STATUS_E_REG 0x3F |
| |
| /* Status bits and masks */ |
| #define CHG_STATUS_MASK SMB349_MASK(2, 1) |
| #define CHG_ENABLE_STATUS_BIT BIT(0) |
| |
| /* Control bits and masks */ |
| #define FAST_CHG_CURRENT_MASK SMB349_MASK(4, 4) |
| #define AC_INPUT_CURRENT_LIMIT_MASK SMB349_MASK(4, 0) |
| #define PRE_CHG_CURRENT_MASK SMB349_MASK(3, 5) |
| #define TERMINATION_CURRENT_MASK SMB349_MASK(3, 2) |
| #define PRE_CHG_TO_FAST_CHG_THRESH_MASK SMB349_MASK(2, 6) |
| #define FLOAT_VOLTAGE_MASK SMB349_MASK(6, 0) |
| #define CHG_ENABLE_BIT BIT(1) |
| #define VOLATILE_W_PERM_BIT BIT(7) |
| #define USB_SELECTION_BIT BIT(1) |
| #define SYSTEM_FET_ENABLE_BIT BIT(7) |
| #define AUTOMATIC_INPUT_CURR_LIMIT_BIT BIT(4) |
| #define AUTOMATIC_POWER_SOURCE_DETECTION_BIT BIT(2) |
| #define BATT_OV_END_CHG_BIT BIT(1) |
| #define VCHG_FUNCTION BIT(0) |
| #define CURR_TERM_END_CHG_BIT BIT(6) |
| |
| struct smb349_struct { |
| struct i2c_client *client; |
| bool charging; |
| bool present; |
| int chg_current_ma; |
| |
| int en_n_gpio; |
| int chg_susp_gpio; |
| struct dentry *dent; |
| spinlock_t lock; |
| |
| struct work_struct chg_work; |
| struct power_supply dc_psy; |
| }; |
| |
| struct chg_ma_limit_entry { |
| int fast_chg_ma_limit; |
| int ac_input_ma_limit; |
| u8 chg_current_value; |
| }; |
| |
| static struct smb349_struct *the_smb349_chg; |
| |
| static int smb349_read_reg(struct i2c_client *client, int reg, |
| u8 *val) |
| { |
| s32 ret; |
| struct smb349_struct *smb349_chg; |
| |
| smb349_chg = i2c_get_clientdata(client); |
| ret = i2c_smbus_read_byte_data(smb349_chg->client, reg); |
| if (ret < 0) { |
| dev_err(&smb349_chg->client->dev, |
| "i2c read fail: can't read from %02x: %d\n", reg, ret); |
| return ret; |
| } else { |
| *val = ret; |
| } |
| |
| return 0; |
| } |
| |
| static int smb349_write_reg(struct i2c_client *client, int reg, |
| u8 val) |
| { |
| s32 ret; |
| struct smb349_struct *smb349_chg; |
| |
| smb349_chg = i2c_get_clientdata(client); |
| ret = i2c_smbus_write_byte_data(smb349_chg->client, reg, val); |
| if (ret < 0) { |
| dev_err(&smb349_chg->client->dev, |
| "i2c write fail: can't write %02x to %02x: %d\n", |
| val, reg, ret); |
| return ret; |
| } |
| return 0; |
| } |
| |
| static int smb349_masked_write(struct i2c_client *client, int reg, |
| u8 mask, u8 val) |
| { |
| s32 rc; |
| u8 temp; |
| |
| rc = smb349_read_reg(client, reg, &temp); |
| if (rc) { |
| pr_err("smb349_read_reg failed: reg=%03X, rc=%d\n", reg, rc); |
| return rc; |
| } |
| temp &= ~mask; |
| temp |= val & mask; |
| rc = smb349_write_reg(client, reg, temp); |
| if (rc) { |
| pr_err("smb349_write failed: reg=%03X, rc=%d\n", reg, rc); |
| return rc; |
| } |
| return 0; |
| } |
| |
| static enum power_supply_property pm_power_props[] = { |
| POWER_SUPPLY_PROP_ONLINE, |
| POWER_SUPPLY_PROP_CURRENT_MAX, |
| POWER_SUPPLY_PROP_CHARGE_TYPE, |
| }; |
| |
| static char *pm_power_supplied_to[] = { |
| "battery", |
| }; |
| |
| static int get_prop_charge_type(struct smb349_struct *smb349_chg) |
| { |
| if (smb349_chg->charging) |
| return POWER_SUPPLY_CHARGE_TYPE_FAST; |
| |
| return POWER_SUPPLY_CHARGE_TYPE_NONE; |
| } |
| |
| static int pm_power_get_property(struct power_supply *psy, |
| enum power_supply_property psp, |
| union power_supply_propval *val) |
| { |
| struct smb349_struct *smb349_chg = container_of(psy, |
| struct smb349_struct, |
| dc_psy); |
| |
| switch (psp) { |
| case POWER_SUPPLY_PROP_CURRENT_MAX: |
| val->intval = smb349_chg->chg_current_ma; |
| break; |
| case POWER_SUPPLY_PROP_ONLINE: |
| val->intval = (int)smb349_chg->present; |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_TYPE: |
| val->intval = get_prop_charge_type(smb349_chg); |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| #define SMB349_FAST_CHG_MIN_MA 1000 |
| #define SMB349_FAST_CHG_STEP_MA 200 |
| #define SMB349_FAST_CHG_MAX_MA 4000 |
| #define SMB349_FAST_CHG_SHIFT 4 |
| static int chg_current_set(struct smb349_struct *smb349_chg) |
| { |
| u8 temp; |
| |
| if ((smb349_chg->chg_current_ma < SMB349_FAST_CHG_MIN_MA) || |
| (smb349_chg->chg_current_ma > SMB349_FAST_CHG_MAX_MA)) { |
| pr_err("bad mA=%d asked to set\n", smb349_chg->chg_current_ma); |
| return -EINVAL; |
| } |
| |
| temp = (smb349_chg->chg_current_ma - SMB349_FAST_CHG_MIN_MA) |
| / SMB349_FAST_CHG_STEP_MA; |
| |
| temp = temp << SMB349_FAST_CHG_SHIFT; |
| pr_debug("fastchg limit=%d setting %02x\n", |
| smb349_chg->chg_current_ma, temp); |
| return smb349_masked_write(smb349_chg->client, CHG_CURRENT_REG, |
| FAST_CHG_CURRENT_MASK, temp); |
| } |
| |
| static int set_reg(void *data, u64 val) |
| { |
| int addr = (int)data; |
| int ret; |
| u8 temp; |
| |
| temp = (u16) val; |
| ret = smb349_write_reg(the_smb349_chg->client, addr, temp); |
| |
| if (ret) { |
| pr_err("smb349_write_reg to %x value =%d errored = %d\n", |
| addr, temp, ret); |
| return -EAGAIN; |
| } |
| return 0; |
| } |
| static int get_reg(void *data, u64 *val) |
| { |
| int addr = (int)data; |
| int ret; |
| u8 temp = 0; |
| |
| ret = smb349_read_reg(the_smb349_chg->client, addr, &temp); |
| if (ret) { |
| pr_err("smb349_read_reg to %x value =%d errored = %d\n", |
| addr, temp, ret); |
| return -EAGAIN; |
| } |
| |
| *val = temp; |
| return 0; |
| } |
| |
| DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n"); |
| |
| static void create_debugfs_entries(struct smb349_struct *smb349_chg) |
| { |
| struct dentry *file; |
| smb349_chg->dent = debugfs_create_dir(SMB349_NAME, NULL); |
| if (IS_ERR(smb349_chg->dent)) { |
| pr_err("smb349 driver couldn't create debugfs dir\n"); |
| return; |
| } |
| |
| file = debugfs_create_file("CHG_CURRENT_REG", 0644, smb349_chg->dent, |
| (void *) CHG_CURRENT_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("CHG_OTHER_CURRENT_REG", 0644, |
| smb349_chg->dent, (void *) CHG_OTHER_CURRENT_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("VAR_FUNC_REG", 0644, smb349_chg->dent, |
| (void *) VAR_FUNC_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("FLOAT_VOLTAGE_REG", 0644, smb349_chg->dent, |
| (void *) FLOAT_VOLTAGE_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("CHG_CTRL_REG", 0644, smb349_chg->dent, |
| (void *) CHG_CTRL_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("STAT_TIMER_REG", 0644, smb349_chg->dent, |
| (void *) STAT_TIMER_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644, |
| smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644, |
| smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644, |
| smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("THERM_CTRL_A_REG", 0644, smb349_chg->dent, |
| (void *) THERM_CTRL_A_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("SYSOK_USB3_SELECT_REG", 0644, |
| smb349_chg->dent, (void *) SYSOK_USB3_SELECT_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("CTRL_FUNCTIONS_REG", 0644, |
| smb349_chg->dent, (void *) CTRL_FUNCTIONS_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("OTG_TLIM_THERM_CNTRL_REG", 0644, |
| smb349_chg->dent, (void *) OTG_TLIM_THERM_CNTRL_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG", |
| 0644, smb349_chg->dent, |
| (void *) HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("SYSOK_REG", 0644, smb349_chg->dent, |
| (void *) SYSOK_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("CMD_A_REG", 0644, smb349_chg->dent, |
| (void *) CMD_A_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("CMD_B_REG", 0644, smb349_chg->dent, |
| (void *) CMD_B_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("CMD_C_REG", 0644, smb349_chg->dent, |
| (void *) CMD_C_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("STATUS_A_REG", 0644, smb349_chg->dent, |
| (void *) STATUS_A_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("STATUS_B_REG", 0644, smb349_chg->dent, |
| (void *) STATUS_B_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("STATUS_C_REG", 0644, smb349_chg->dent, |
| (void *) STATUS_C_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("STATUS_D_REG", 0644, smb349_chg->dent, |
| (void *) STATUS_D_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| file = debugfs_create_file("STATUS_E_REG", 0644, smb349_chg->dent, |
| (void *) STATUS_E_REG, ®_fops); |
| if (IS_ERR(file)) { |
| pr_err("smb349 driver couldn't create debugfs files\n"); |
| return; |
| } |
| } |
| |
| static void remove_debugfs_entries(struct smb349_struct *smb349_chg) |
| { |
| if (smb349_chg->dent) |
| debugfs_remove_recursive(smb349_chg->dent); |
| } |
| |
| static int smb349_hwinit(struct smb349_struct *smb349_chg) |
| { |
| int ret; |
| |
| ret = smb349_write_reg(smb349_chg->client, CMD_A_REG, |
| VOLATILE_W_PERM_BIT); |
| if (ret) { |
| pr_err("Failed to set VOLATILE_W_PERM_BIT rc=%d\n", ret); |
| return ret; |
| } |
| |
| ret = smb349_masked_write(smb349_chg->client, CHG_CTRL_REG, |
| CURR_TERM_END_CHG_BIT, CURR_TERM_END_CHG_BIT); |
| if (ret) { |
| pr_err("Failed to set CURR_TERM_END_CHG_BIT rc=%d\n", ret); |
| return ret; |
| } |
| |
| ret = chg_current_set(smb349_chg); |
| if (ret) { |
| pr_err("Failed to set FAST_CHG_CURRENT rc=%d\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int smb349_stop_charging(struct smb349_struct *smb349_chg) |
| { |
| unsigned long flags; |
| int rc = 0; |
| |
| spin_lock_irqsave(&smb349_chg->lock, flags); |
| pr_debug("stop charging %d\n", smb349_chg->charging); |
| smb349_chg->charging = 0; |
| spin_unlock_irqrestore(&smb349_chg->lock, flags); |
| |
| if (smb349_chg->charging) |
| rc = schedule_work(&smb349_chg->chg_work); |
| |
| return rc; |
| } |
| |
| static int smb349_start_charging(struct smb349_struct *smb349_chg) |
| { |
| unsigned long flags; |
| int rc; |
| |
| rc = 0; |
| spin_lock_irqsave(&smb349_chg->lock, flags); |
| pr_debug("start charging %d\n", smb349_chg->charging); |
| smb349_chg->charging = 1; |
| spin_unlock_irqrestore(&smb349_chg->lock, flags); |
| |
| if (!smb349_chg->charging) |
| rc = schedule_work(&smb349_chg->chg_work); |
| |
| return rc; |
| } |
| |
| static int pm_power_set_property(struct power_supply *psy, |
| enum power_supply_property psp, |
| const union power_supply_propval *val) |
| { |
| struct smb349_struct *smb349_chg = container_of(psy, |
| struct smb349_struct, |
| dc_psy); |
| |
| switch (psp) { |
| case POWER_SUPPLY_PROP_ONLINE: |
| if (val->intval) { |
| smb349_chg->present = val->intval; |
| } else { |
| smb349_chg->present = 0; |
| if (smb349_chg->charging) |
| return smb349_stop_charging(smb349_chg); |
| } |
| break; |
| case POWER_SUPPLY_PROP_CURRENT_MAX: |
| if (val->intval) { |
| if (smb349_chg->chg_current_ma != val->intval) |
| return -EINVAL; |
| } |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_TYPE: |
| if (val->intval && smb349_chg->present) { |
| if (val->intval == POWER_SUPPLY_CHARGE_TYPE_FAST) |
| return smb349_start_charging(smb349_chg); |
| if (val->intval == POWER_SUPPLY_CHARGE_TYPE_NONE) |
| return smb349_stop_charging(smb349_chg); |
| } else { |
| return -EINVAL; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| power_supply_changed(&smb349_chg->dc_psy); |
| return 0; |
| } |
| |
| static void chg_worker(struct work_struct *work) |
| { |
| struct smb349_struct *smb349_chg = container_of(work, |
| struct smb349_struct, chg_work); |
| int ret = 0; |
| |
| gpio_set_value_cansleep(smb349_chg->en_n_gpio, smb349_chg->charging); |
| |
| /* |
| * Write non-default values, charger chip reloads from |
| * non-volatile memory if it was in suspend mode |
| * |
| */ |
| if (smb349_chg->charging) |
| ret = smb349_hwinit(smb349_chg); |
| if (ret) |
| pr_err("Failed to re-initilaze registers\n"); |
| |
| power_supply_changed(&smb349_chg->dc_psy); |
| } |
| |
| static int __devinit smb349_init_ext_chg(struct smb349_struct *smb349_chg) |
| { |
| int ret; |
| |
| smb349_chg->dc_psy.name = "dc"; |
| smb349_chg->dc_psy.type = POWER_SUPPLY_TYPE_MAINS; |
| smb349_chg->dc_psy.supplied_to = pm_power_supplied_to; |
| smb349_chg->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to); |
| smb349_chg->dc_psy.properties = pm_power_props; |
| smb349_chg->dc_psy.num_properties = ARRAY_SIZE(pm_power_props); |
| smb349_chg->dc_psy.get_property = pm_power_get_property; |
| smb349_chg->dc_psy.set_property = pm_power_set_property; |
| |
| ret = power_supply_register(&smb349_chg->client->dev, |
| &smb349_chg->dc_psy); |
| if (ret) { |
| pr_err("failed to register power_supply. ret=%d.\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int __devinit smb349_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| const struct smb349_platform_data *pdata; |
| struct smb349_struct *smb349_chg; |
| int ret = 0; |
| |
| pdata = client->dev.platform_data; |
| |
| if (pdata == NULL) { |
| dev_err(&client->dev, "%s no platform data\n", __func__); |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| if (!i2c_check_functionality(client->adapter, |
| I2C_FUNC_SMBUS_BYTE_DATA)) { |
| ret = -EIO; |
| goto out; |
| } |
| |
| smb349_chg = kzalloc(sizeof(*smb349_chg), GFP_KERNEL); |
| if (!smb349_chg) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| smb349_chg->client = client; |
| smb349_chg->chg_current_ma = pdata->chg_current_ma; |
| ret = gpio_request(pdata->chg_susp_gpio, "smb349_suspend"); |
| if (ret) { |
| dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n", |
| __func__, pdata->chg_susp_gpio, ret); |
| goto free_smb349_chg; |
| } |
| smb349_chg->chg_susp_gpio = pdata->chg_susp_gpio; |
| |
| ret = gpio_request(pdata->en_n_gpio, "smb349_charger_enable"); |
| if (ret) { |
| dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n", |
| __func__, pdata->en_n_gpio, ret); |
| goto chg_susp_gpio_fail; |
| } |
| smb349_chg->en_n_gpio = pdata->en_n_gpio; |
| |
| i2c_set_clientdata(client, smb349_chg); |
| |
| ret = smb349_hwinit(smb349_chg); |
| if (ret) |
| goto free_smb349_chg; |
| |
| ret = smb349_init_ext_chg(smb349_chg); |
| if (ret) |
| goto chg_en_gpio_fail; |
| |
| the_smb349_chg = smb349_chg; |
| |
| spin_lock_init(&smb349_chg->lock); |
| |
| create_debugfs_entries(smb349_chg); |
| INIT_WORK(&smb349_chg->chg_work, chg_worker); |
| |
| pr_info("OK connector present = %d\n", smb349_chg->present); |
| return 0; |
| |
| chg_en_gpio_fail: |
| gpio_free(smb349_chg->en_n_gpio); |
| chg_susp_gpio_fail: |
| gpio_free(smb349_chg->chg_susp_gpio); |
| free_smb349_chg: |
| kfree(smb349_chg); |
| out: |
| return ret; |
| } |
| |
| static int __devexit smb349_remove(struct i2c_client *client) |
| { |
| const struct smb349_platform_data *pdata; |
| struct smb349_struct *smb349_chg = i2c_get_clientdata(client); |
| |
| flush_work(&smb349_chg->chg_work); |
| pdata = client->dev.platform_data; |
| power_supply_unregister(&smb349_chg->dc_psy); |
| gpio_free(pdata->en_n_gpio); |
| gpio_free(pdata->chg_susp_gpio); |
| remove_debugfs_entries(smb349_chg); |
| kfree(smb349_chg); |
| return 0; |
| } |
| |
| static int smb349_suspend(struct device *dev) |
| { |
| struct smb349_struct *smb349_chg = dev_get_drvdata(dev); |
| |
| pr_debug("suspend\n"); |
| if (smb349_chg->charging) |
| return -EBUSY; |
| return 0; |
| } |
| |
| static int smb349_resume(struct device *dev) |
| { |
| pr_debug("resume\n"); |
| |
| return 0; |
| } |
| |
| static const struct dev_pm_ops smb349_pm_ops = { |
| .suspend = smb349_suspend, |
| .resume = smb349_resume, |
| }; |
| |
| static const struct i2c_device_id smb349_id[] = { |
| {SMB349_NAME, 0}, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(i2c, smb349_id); |
| |
| static struct i2c_driver smb349_driver = { |
| .driver = { |
| .name = SMB349_NAME, |
| .owner = THIS_MODULE, |
| .pm = &smb349_pm_ops, |
| }, |
| .probe = smb349_probe, |
| .remove = __devexit_p(smb349_remove), |
| .id_table = smb349_id, |
| }; |
| |
| static int __init smb349_init(void) |
| { |
| return i2c_add_driver(&smb349_driver); |
| } |
| module_init(smb349_init); |
| |
| static void __exit smb349_exit(void) |
| { |
| return i2c_del_driver(&smb349_driver); |
| } |
| module_exit(smb349_exit); |
| |
| MODULE_DESCRIPTION("Driver for SMB349 charger chip"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_ALIAS("i2c:" SMB349_NAME); |