/* * da9155_charger.c * Samsung da9155 Charger Driver * * Copyright (C) 2015 Samsung Electronics * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ #include #include #include #define DEBUG #define ENABLE 1 #define DISABLE 0 static void da9155_set_charge_current(struct da9155_charger_data *charger, int charge_current); static enum power_supply_property da9155_charger_props[] = { }; static int da9155_read_reg(struct i2c_client *client, u8 reg, u8 *dest) { struct da9155_charger_data *charger = i2c_get_clientdata(client); int ret = 0; mutex_lock(&charger->io_lock); ret = i2c_smbus_read_byte_data(client, reg); mutex_unlock(&charger->io_lock); if (ret < 0) { pr_err("%s: can't read reg(0x%x), ret(%d)\n", __func__, reg, ret); return ret; } reg &= 0xFF; *dest = ret; return 0; } static int da9155_write_reg(struct i2c_client *client, u8 reg, u8 data) { struct da9155_charger_data *charger = i2c_get_clientdata(client); int ret = 0; mutex_lock(&charger->io_lock); ret = i2c_smbus_write_byte_data(client, reg, data); mutex_unlock(&charger->io_lock); if (ret < 0) pr_err("%s: can't write reg(0x%x), ret(%d)\n", __func__, reg, ret); return ret; } static int da9155_update_reg(struct i2c_client *client, u8 reg, u8 val, u8 mask) { struct da9155_charger_data *charger = i2c_get_clientdata(client); int ret = 0; mutex_lock(&charger->io_lock); ret = i2c_smbus_read_byte_data(client, reg); if (ret < 0) pr_err("%s: can't update reg(0x%x), ret(%d)\n", __func__, reg, ret); else { u8 old_val = ret & 0xFF; u8 new_val = (val & mask) | (old_val & (~mask)); ret = i2c_smbus_write_byte_data(client, reg, new_val); } mutex_unlock(&charger->io_lock); return ret; } static void da9155_charger_test_read(struct da9155_charger_data *charger) { u8 data = 0; u32 addr = 0; char str[1000]={0,}; for (addr = 0x01; addr <= 0x13; addr++) { da9155_read_reg(charger->i2c, addr, &data); sprintf(str + strlen(str), "[0x%02x]0x%02x, ", addr, data); } pr_info("DA9155 : %s\n", str); } static int da9155_get_charger_state(struct da9155_charger_data *charger) { u8 reg_data; da9155_read_reg(charger->i2c, DA9155_STATUS_B, ®_data); if (reg_data & DA9155_MODE_MASK) return POWER_SUPPLY_STATUS_CHARGING; return POWER_SUPPLY_STATUS_NOT_CHARGING; } static int da9155_get_charger_health(struct da9155_charger_data *charger) { u8 reg_data; // 80s same with maxim IC if (da9155_write_reg(charger->i2c, DA9155_TIMER_B, 0x50) < 0) { dev_info(charger->dev, "%s: addr: 0x%x write fail\n", __func__, DA9155_TIMER_B); } da9155_charger_test_read(charger); da9155_read_reg(charger->i2c, DA9155_STATUS_A, ®_data); if (reg_data & DA9155_S_VIN_UV_MASK) { pr_info("%s: VIN undervoltage\n", __func__); return POWER_SUPPLY_HEALTH_UNDERVOLTAGE; } else if (reg_data & DA9155_S_VIN_DROP_MASK) { pr_info("%s: VIN DROP\n", __func__); return POWER_SUPPLY_HEALTH_UNDERVOLTAGE; } else if (reg_data & DA9155_S_VIN_OV_MASK) { pr_info("%s: VIN overvoltage\n", __func__); return POWER_SUPPLY_HEALTH_OVERVOLTAGE; } else return POWER_SUPPLY_HEALTH_GOOD; } #if 0 static int da9155_get_input_current(struct da9155_charger_data *charger) { u8 reg_data; int input_current; da9155_read_reg(charger->i2c, DA9155_BUCK_ILIM, ®_data); input_current = reg_data * 100 + 3000; return input_current; } #endif static int da9155_get_charge_current(struct da9155_charger_data *charger) { u8 reg_data; int charge_current; da9155_read_reg(charger->i2c, DA9155_BUCK_IOUT, ®_data); charge_current = reg_data * 10 + 250; return charge_current; } static void da9155_charger_initialize(struct da9155_charger_data *charger) { pr_info("%s\n", __func__); /* clear event reg */ da9155_update_reg(charger->i2c, DA9155_EVENT_A, 0xFF, 0xFF); da9155_update_reg(charger->i2c, DA9155_EVENT_B, 0xFF, 0xFF); /* Remove Event Mask set. Using Default value (EventA : 0xFF, EventB: 0xFE */ #if 0 /* unmasked: E_VIN_UV, E_VIN_DROP, E_VIN_OV */ da9155_update_reg(charger->i2c, DA9155_MASK_A, DA9155_M_VIN_UV_MASK | DA9155_M_VIN_DROP_MASK | DA9155_M_VIN_OV_MASK, 0xFF); da9155_update_reg(charger->i2c, DA9155_MASK_B, 0, 0xFF); #endif /* Safety timer enable */ da9155_update_reg(charger->i2c, DA9155_CONTROL_E, 0, DA9155_TIMER_DIS_MASK); } static void da9155_set_charger_state(struct da9155_charger_data *charger, int enable) { u8 status = 0; int current_setting = 0; pr_info("%s: BUCK_EN(%s)\n", enable > 0 ? "ENABLE" : "DISABLE", __func__); if (enable){ #if 1 /* DLG */ da9155_charger_initialize(charger); da9155_read_reg(charger->i2c, DA9155_STATUS_A, &status); if (status & 0x7E ){ pr_info("%s: STATUS_A(0x%X)\n", __func__, status); return; } #endif current_setting = da9155_get_charge_current(charger); da9155_set_charge_current(charger, 500); da9155_update_reg(charger->i2c, DA9155_BUCK_CONT, DA9155_BUCK_EN_MASK, DA9155_BUCK_EN_MASK); msleep(10); if (current_setting > 500) da9155_set_charge_current(charger, current_setting); } else { current_setting = da9155_get_charge_current(charger); if (current_setting > 700) { da9155_set_charge_current(charger, 700); msleep(100); } da9155_set_charge_current(charger, 500); msleep(100); da9155_update_reg(charger->i2c, DA9155_BUCK_CONT, 0, DA9155_BUCK_EN_MASK); } } #if 0 static void da9155_set_input_current(struct da9155_charger_data *charger, int input_current) { u8 reg_data; reg_data = (input_current - 3000) / 100; da9155_update_reg(charger->i2c, DA9155_BUCK_ILIM, reg_data, DA9155_BUCK_ILIM_MASK); pr_info("%s: input_current(%d)\n", __func__, input_current); } #endif static void da9155_set_charge_current(struct da9155_charger_data *charger, int charge_current) { u8 reg_data; if (!charge_current) { reg_data = 0x00; } else { charge_current = (charge_current > 2500) ? 2500 : charge_current; reg_data = (charge_current - 250) / 10; } da9155_update_reg(charger->i2c, DA9155_BUCK_IOUT, reg_data, DA9155_BUCK_IOUT_MASK); pr_info("%s: charge_current(%d)\n", __func__, charge_current); } /* return : 0 (AFC type or High Voltage(9V, 12V), 1 : 5V ) */ static int da9155_check_cable_type(unsigned int type) { if (type == POWER_SUPPLY_TYPE_BATTERY) return -1; // AFC or High Voltage TA if (type == POWER_SUPPLY_TYPE_HV_MAINS) { return 0; } else { return 1; } } static void da9155_prevent_vin_drop(struct da9155_charger_data *charger) { /* set VIN_DROP to 4.85V for S/W to prevent reset issue by VIN collapse */ //da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x06); //da9155_write_reg(charger->i2c, 0x39, 0x05); //da9155_write_reg(charger->i2c, 0x11, 0x04); #if 0 /* Enable VBAT_OV and debouncing time */ da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x0); da9155_write_reg(charger->i2c, 0x6A, 0x4A); da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x2); da9155_write_reg(charger->i2c, 0x56, 0x34); da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x0); da9155_write_reg(charger->i2c, 0x6A, 0x00); da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x06); da9155_write_reg(charger->i2c, 0x19, 0x00); da9155_write_reg(charger->i2c, 0x39, 0x00); #endif //da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x0); da9155_write_reg(charger->i2c, DA9155_CONTROL_A, 0xB); //da9155_write_reg(charger->i2c, DA9155_CONTROL_C, 0x28); /* VBAT_OV 4.6V */ } static void da9155_mode_change(struct da9155_charger_data *charger, int enable) { if(charger->i2c) { pr_info("%s: mode(%d), prev_cable_type(%d), cable_type(%d)\n", __func__, enable,charger->prev_cable_type, charger->cable_type); if (enable == ENABLE) { da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x06); da9155_write_reg(charger->i2c, 0x39, 0x05); da9155_write_reg(charger->i2c, 0x11, 0x04); da9155_write_reg(charger->i2c, 0x39, 0x0); da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x0); } else if (enable == DISABLE) { if(charger->prev_cable_type == POWER_SUPPLY_TYPE_HV_MAINS && charger->cable_type == POWER_SUPPLY_TYPE_BATTERY) return; da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x06); da9155_write_reg(charger->i2c, 0x39, 0x05); da9155_write_reg(charger->i2c, 0x11, 0x0); if(charger->prev_cable_type != POWER_SUPPLY_TYPE_HV_MAINS && charger->prev_cable_type != POWER_SUPPLY_TYPE_BATTERY && charger->cable_type == POWER_SUPPLY_TYPE_BATTERY) return; da9155_write_reg(charger->i2c, 0x39, 0x0); da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x0); } } } static irqreturn_t da9155_irq_handler(int irq, void *data) { struct da9155_charger_data *charger = data; u8 event_a, event_b; dev_info(charger->dev, "%s: \n", __func__); if (!da9155_read_reg(charger->i2c, DA9155_EVENT_A, &event_a) && !da9155_read_reg(charger->i2c, DA9155_EVENT_B, &event_b)) { if ((event_b & 0x1) && (charger->cable_type != POWER_SUPPLY_TYPE_BATTERY)) { dev_info(charger->dev, "%s: E_RDY Event occured", __func__); da9155_mode_change(charger, da9155_check_cable_type(charger->cable_type)); da9155_charger_initialize(charger); da9155_prevent_vin_drop(charger); } /* clear event reg */ da9155_write_reg(charger->i2c, DA9155_EVENT_A, event_a); da9155_write_reg(charger->i2c, DA9155_EVENT_B, event_b); dev_info(charger->dev, "%s: EVENT_A(0x%x), EVENT_B(0x%x)\n", __func__, event_a, event_b); } return IRQ_HANDLED; } static int da9155_chg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); int ret=0; u8 reg_data; if (charger->cable_type == POWER_SUPPLY_TYPE_BATTERY && psp != POWER_SUPPLY_PROP_READ_SLAVE_REG) { pr_info("%s: skip get_property(psp type = %d)\n", __func__, psp); return -EINVAL; } switch (psp) { case POWER_SUPPLY_PROP_HEALTH: val->intval = da9155_get_charger_health(charger); break; case POWER_SUPPLY_PROP_ONLINE: val->intval = da9155_get_charger_state(charger); break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = da9155_get_charge_current(charger); break; case POWER_SUPPLY_PROP_READ_SLAVE_REG: ret = da9155_read_reg(charger->i2c, DA9155_EVENT_B, ®_data); if(ret<0) val->intval = 0; else val->intval = 1; pr_info("%s : READ_SLAVE_REG val= %d ret=%d\n",__func__,val->intval,ret); break; case POWER_SUPPLY_PROP_CURRENT_MAX: return -ENODATA; default: return -EINVAL; } return 0; } static int da9155_chg_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); pr_info("%s %d\n",__func__,psp); switch (psp) { case POWER_SUPPLY_PROP_CHARGING_ENABLED: charger->is_charging = (val->intval == SEC_BAT_CHG_MODE_CHARGING) ? ENABLE : DISABLE; da9155_set_charger_state(charger, charger->is_charging); break; case POWER_SUPPLY_PROP_CURRENT_NOW: charger->charging_current = val->intval; da9155_set_charge_current(charger, charger->charging_current); break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: charger->siop_level = val->intval; break; case POWER_SUPPLY_PROP_ONLINE: charger->cable_type = val->intval; if (val->intval != POWER_SUPPLY_TYPE_BATTERY) { da9155_mode_change(charger, da9155_check_cable_type(charger->cable_type)); charger->prev_cable_type = charger ->cable_type; da9155_charger_initialize(charger); da9155_prevent_vin_drop(charger); } else { da9155_mode_change(charger, DISABLE); charger->prev_cable_type = POWER_SUPPLY_TYPE_BATTERY; } break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_CURRENT_FULL: break; case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_HEALTH: return -ENODATA; default: return -EINVAL; } return 0; } static int da9155_charger_parse_dt(struct da9155_charger_data *charger, struct da9155_charger_platform_data *pdata) { struct device_node *np = of_find_node_by_name(NULL, "da9155-charger"); int ret = 0; if (!np) { pr_err("%s: np is NULL\n", __func__); return -1; } else { ret = of_get_named_gpio_flags(np, "da9155-charger,irq-gpio", 0, NULL); if (ret < 0) { pr_err("%s: da9155-charger,irq-gpio is empty\n", __func__); pdata->irq_gpio = 0; } else { pdata->irq_gpio = ret; pr_info("%s: irq-gpio = %d\n", __func__, pdata->irq_gpio); } } np = of_find_node_by_name(NULL, "battery"); if (!np) { pr_err("%s np NULL\n", __func__); } else { ret = of_property_read_u32(np, "battery,chg_float_voltage", &charger->float_voltage); if (ret) { pr_info("%s: battery,chg_float_voltage is Empty\n", __func__); charger->float_voltage = 42000; } } return ret; } static ssize_t da9155_store_addr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); int x; if (sscanf(buf, "0x%x\n", &x) == 1) { charger->addr = x; } return count; } static ssize_t da9155_show_addr(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); return sprintf(buf, "0x%x\n", charger->addr); } static ssize_t da9155_store_size(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); int x; if (sscanf(buf, "%d\n", &x) == 1) { charger->size = x; } return count; } static ssize_t da9155_show_size(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); return sprintf(buf, "0x%x\n", charger->size); } static ssize_t da9155_store_data(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); int x; if (sscanf(buf, "0x%x", &x) == 1) { u8 data = x; if (da9155_write_reg(charger->i2c, charger->addr, data) < 0) { dev_info(charger->dev, "%s: addr: 0x%x write fail\n", __func__, charger->addr); } } return count; } static ssize_t da9155_show_data(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct da9155_charger_data *charger = container_of(psy, struct da9155_charger_data, psy_chg); u8 data; int i, count = 0;; if (charger->size == 0) charger->size = 1; for (i = 0; i < charger->size; i++) { if (da9155_read_reg(charger->i2c, charger->addr+i, &data) < 0) { dev_info(charger->dev, "%s: read fail\n", __func__); count += sprintf(buf+count, "addr: 0x%x read fail\n", charger->addr+i); continue; } count += sprintf(buf+count, "addr: 0x%x, data: 0x%x\n", charger->addr+i,data); } return count; } static DEVICE_ATTR(addr, 0644, da9155_show_addr, da9155_store_addr); static DEVICE_ATTR(size, 0644, da9155_show_size, da9155_store_size); static DEVICE_ATTR(data, 0644, da9155_show_data, da9155_store_data); static struct attribute *da9155_attributes[] = { &dev_attr_addr.attr, &dev_attr_size.attr, &dev_attr_data.attr, NULL }; static const struct attribute_group da9155_attr_group = { .attrs = da9155_attributes, }; static int da9155_charger_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device_node *of_node = client->dev.of_node; struct da9155_charger_data *charger; struct da9155_charger_platform_data *pdata = client->dev.platform_data; int ret = 0; #if 1 /* DLG */ //u8 data = 0; #endif pr_info("%s: DA9155 Charger Driver Loading\n", __func__); charger = kzalloc(sizeof(*charger), GFP_KERNEL); if (!charger) { pr_err("%s: Failed to allocate memory\n", __func__); return -ENOMEM; } mutex_init(&charger->io_lock); charger->dev = &client->dev; charger->i2c = client; if (of_node) { pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { pr_err("%s: Failed to allocate memory\n", __func__); ret = -ENOMEM; goto err_nomem; } ret = da9155_charger_parse_dt(charger, pdata); if (ret < 0) goto err_parse_dt; } else { pdata = client->dev.platform_data; } charger->pdata = pdata; i2c_set_clientdata(client, charger); charger->psy_chg.name = "da9155-charger"; charger->psy_chg.type = POWER_SUPPLY_TYPE_UNKNOWN; charger->psy_chg.get_property = da9155_chg_get_property; charger->psy_chg.set_property = da9155_chg_set_property; charger->psy_chg.properties = da9155_charger_props; charger->psy_chg.num_properties = ARRAY_SIZE(da9155_charger_props); /* da9155_charger_initialize(charger); */ charger->cable_type = POWER_SUPPLY_TYPE_BATTERY; charger->prev_cable_type = POWER_SUPPLY_TYPE_BATTERY; ret = power_supply_register(charger->dev, &charger->psy_chg); if (ret) { pr_err("%s: Failed to Register psy_chg\n", __func__); ret = -1; goto err_power_supply_register; } charger->wqueue = create_singlethread_workqueue(dev_name(charger->dev)); if (!charger->wqueue) { pr_err("%s: Fail to Create Workqueue\n", __func__); ret = -1; goto err_create_wqueue; } if (pdata->irq_gpio) { charger->chg_irq = gpio_to_irq(pdata->irq_gpio); ret = request_threaded_irq(charger->chg_irq, NULL, da9155_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "da9155-irq", charger); if (ret < 0) { pr_err("%s: Failed to Request IRQ(%d)\n", __func__, ret); goto err_req_irq; } #if 0 /* DLG */ if (!gpio_get_value(pdata->irq_gpio)) da9155_irq_handler(charger->chg_irq, charger); #else //ret = da9155_read_reg(charger->i2c, DA9155_PAGE_CTRL_0, &data); //if (ret == 0) { // da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x06); // da9155_read_reg(charger->i2c, 0x11, &data); // da9155_write_reg(charger->i2c, DA9155_PAGE_CTRL_0, 0x0); // pr_info("%s: mode(%d)\n", __func__, data); // da9155_mode_change(charger, DISABLE); //} #endif } device_init_wakeup(charger->dev, 1); ret = sysfs_create_group(&charger->psy_chg.dev->kobj, &da9155_attr_group); if (ret) { dev_info(&client->dev, "%s: sysfs_create_group failed\n", __func__); } pr_info("%s: DA9155 Charger Driver Loaded\n", __func__); return 0; err_req_irq: err_create_wqueue: power_supply_unregister(&charger->psy_chg); err_power_supply_register: mutex_destroy(&charger->io_lock); err_parse_dt: kfree(pdata); err_nomem: kfree(charger); return ret; } static int da9155_charger_remove(struct i2c_client *client) { struct da9155_charger_data *charger = i2c_get_clientdata(client); if (charger->chg_irq) free_irq(charger->chg_irq, charger); destroy_workqueue(charger->wqueue); power_supply_unregister(&charger->psy_chg); mutex_destroy(&charger->io_lock); kfree(charger->pdata); kfree(charger); return 0; } static void da9155_charger_shutdown(struct i2c_client *client) { struct da9155_charger_data *charger = i2c_get_clientdata(client); if (charger->chg_irq) free_irq(charger->chg_irq, charger); da9155_update_reg(client, DA9155_BUCK_CONT, 0, DA9155_BUCK_EN_MASK); da9155_update_reg(client, DA9155_BUCK_IOUT, 0x7D, DA9155_BUCK_ILIM_MASK); if (charger->cable_type != POWER_SUPPLY_TYPE_BATTERY) da9155_mode_change(charger, DISABLE); } static const struct i2c_device_id da9155_charger_id_table[] = { {"da9155-charger", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, da9155_id_table); #ifdef CONFIG_OF static struct of_device_id da9155_charger_match_table[] = { {.compatible = "dlg,da9155-charger"}, {}, }; #else #define da9155_charger_match_table NULL #endif #if defined(CONFIG_PM) static int da9155_charger_suspend(struct device *dev) { struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct da9155_charger_data *charger = i2c_get_clientdata(i2c); if (charger->chg_irq) { if (device_may_wakeup(dev)) enable_irq_wake(charger->chg_irq); disable_irq(charger->chg_irq); } return 0; } static int da9155_charger_resume(struct device *dev) { struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct da9155_charger_data *charger = i2c_get_clientdata(i2c); if (charger->chg_irq) { if (device_may_wakeup(dev)) disable_irq_wake(charger->chg_irq); enable_irq(charger->chg_irq); } return 0; } #else #define da9155_charger_suspend NULL #define da9155_charger_resume NULL #endif /* CONFIG_PM */ const struct dev_pm_ops da9155_pm = { .suspend = da9155_charger_suspend, .resume = da9155_charger_resume, }; static struct i2c_driver da9155_charger_driver = { .driver = { .name = "da9155-charger", .owner = THIS_MODULE, #if defined(CONFIG_PM) .pm = &da9155_pm, #endif /* CONFIG_PM */ .of_match_table = da9155_charger_match_table, }, .probe = da9155_charger_probe, .remove = da9155_charger_remove, .shutdown = da9155_charger_shutdown, .id_table = da9155_charger_id_table, }; static int __init da9155_charger_init(void) { pr_info("%s: \n", __func__); return i2c_add_driver(&da9155_charger_driver); } static void __exit da9155_charger_exit(void) { pr_info("%s: \n", __func__); i2c_del_driver(&da9155_charger_driver); } module_init(da9155_charger_init); module_exit(da9155_charger_exit); MODULE_DESCRIPTION("Samsung DA9155 Charger Driver"); MODULE_AUTHOR("Samsung Electronics"); MODULE_LICENSE("GPL");