From ff8bf393c78bbeadd92fab12584818623889c3c7 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Tue, 2 Apr 2013 17:08:58 +0900 Subject: [PATCH] power: max77693: Add max77693 charger dirver. Signed-off-by: Jonghwa Lee --- drivers/power/Makefile | 2 + drivers/power/max77693_charger.c | 689 +++++++++++++++++++++++++++++++++ include/linux/mfd/max77693.h | 8 + include/linux/power/max77693_charger.h | 177 +++++++++ 4 files changed, 876 insertions(+) create mode 100644 drivers/power/max77693_charger.c create mode 100644 include/linux/power/max77693_charger.h diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 653bf6c..a917c98 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -49,6 +49,8 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o +obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o +obj-y += max77693_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c new file mode 100644 index 0000000..340aec0 --- /dev/null +++ b/drivers/power/max77693_charger.c @@ -0,0 +1,689 @@ +/* + * max77693_charger.c + * + * Copyright (C) 2011 Samsung Electronics + * SangYoung Son + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * Simplified hello.son's driver removing unnecessary features for + * charger-manager; supporting power-supply-class is the main role. + * + * 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. + * + * "Input current" in the original driver is controlled by regulator + * "CHARGER". + * "Charge current" in the original driver is controlled by regulator + * "CHARGER_CC". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MAX77693 Registers(defined @max77693-private.h) */ + +/* MAX77693_CHG_REG_CHG_INT */ +#define MAX77693_BYP_I (1 << 0) +#define MAX77693_THM_I (1 << 2) +#define MAX77693_BAT_I (1 << 3) +#define MAX77693_CHG_I (1 << 4) +#define MAX77693_CHGIN_I (1 << 6) + +/* MAX77693_CHG_REG_CHG_INT_MASK */ +#define MAX77693_BYP_IM (1 << 0) +#define MAX77693_THM_IM (1 << 2) +#define MAX77693_BAT_IM (1 << 3) +#define MAX77693_CHG_IM (1 << 4) +#define MAX77693_CHGIN_IM (1 << 6) + +/* MAX77693_CHG_REG_CHG_INT_OK */ +#define MAX77693_BYP_OK 0x01 +#define MAX77693_BYP_OK_SHIFT 0 +#define MAX77693_THM_OK 0x04 +#define MAX77693_THM_OK_SHIFT 2 +#define MAX77693_BAT_OK 0x08 +#define MAX77693_BAT_OK_SHIFT 3 +#define MAX77693_CHG_OK 0x10 +#define MAX77693_CHG_OK_SHIFT 4 +#define MAX77693_CHGIN_OK 0x40 +#define MAX77693_CHGIN_OK_SHIFT 6 +#define MAX77693_DETBAT 0x80 +#define MAX77693_DETBAT_SHIFT 7 + +/* MAX77693_CHG_REG_CHG_DTLS_00 */ +#define MAX77693_THM_DTLS 0x07 +#define MAX77693_THM_DTLS_SHIFT 0 +#define MAX77693_CHGIN_DTLS 0x60 +#define MAX77693_CHGIN_DTLS_SHIFT 5 + +/* MAX77693_CHG_REG_CHG_DTLS_01 */ +#define MAX77693_CHG_DTLS 0x0F +#define MAX77693_CHG_DTLS_SHIFT 0 +#define MAX77693_BAT_DTLS 0x70 +#define MAX77693_BAT_DTLS_SHIFT 4 + +/* MAX77693_CHG_REG_CHG_DTLS_02 */ +#define MAX77693_BYP_DTLS 0x0F +#define MAX77693_BYP_DTLS_SHIFT 0 +#define MAX77693_BYP_DTLS0 0x1 +#define MAX77693_BYP_DTLS1 0x2 +#define MAX77693_BYP_DTLS2 0x4 +#define MAX77693_BYP_DTLS3 0x8 + +/* MAX77693_CHG_REG_CHG_CNFG_00 */ +#define MAX77693_MODE_DEFAULT 0x04 +#define MAX77693_MODE_CHGR 0x01 +#define MAX77693_MODE_OTG 0x02 +#define MAX77693_MODE_BUCK 0x04 + +/* MAX77693_CHG_REG_CHG_CNFG_02 */ +#define MAX77693_CHG_CC 0x3F + +/* MAX77693_CHG_REG_CHG_CNFG_04 */ +#define MAX77693_CHG_MINVSYS_MASK 0xE0 +#define MAX77693_CHG_MINVSYS_SHIFT 5 +#define MAX77693_CHG_MINVSYS_3_6V 0x06 +#define MAX77693_CHG_CV_PRM_MASK 0x1F +#define MAX77693_CHG_CV_PRM_SHIFT 0 +#define MAX77693_CHG_CV_PRM_4_20V 0x16 +#define MAX77693_CHG_CV_PRM_4_35V 0x1D +#define MAX77693_CHG_CV_PRM_4_40V 0x1F + +/* MAX77693_CHG_REG_CHG_CNFG_06 */ +#define MAX77693_CHG_CHGPROT 0x0C +#define MAX77693_CHG_CHGPROT_SHIFT 2 +#define MAX77693_CHG_CHGPROT_UNLOCK 0x03 + +/* MAX77693_CHG_REG_CHG_CNFG_09 */ +#define MAX77693_CHG_CHGIN_LIM 0x7F + +/* MAX77693_MUIC_REG_CDETCTRL1 */ +#define MAX77693_CHGTYPMAN 0x02 +#define MAX77693_CHGTYPMAN_SHIFT 1 + +/* MAX77693_MUIC_REG_STATUS2 */ +#define MAX77693_VBVOLT 0x40 +#define MAX77693_VBVOLT_SHIFT 6 +#define MAX77693_DXOVP 0x20 +#define MAX77693_DXOVP_SHIFT 5 +#define MAX77693_CHGDETRUN 0x08 +#define MAX77693_CHGDETRUN_SHIFT 3 +#define MAX77693_CHGTYPE 0x07 +#define MAX77693_CHGTYPE_SHIFT 0 + +/* irq */ +#define IRQ_DEBOUNCE_TIME 20 /* msec */ + +/* charger unlock */ +#define CHG_UNLOCK_RETRY 10 +#define CHG_UNLOCK_DELAY 100 + +/* power stabe guarantee */ +#define STABLE_POWER_DELAY 500 + +/* charger type detection */ +#define DET_ERR_RETRY 5 +#define DET_ERR_DELAY 200 + +/* soft charging */ +#define SOFT_CHG_START_CURR 100 /* mA */ +#define SOFT_CHG_START_DUR 100 /* ms */ +#define SOFT_CHG_CURR_STEP 100 /* mA */ +#define SOFT_CHG_STEP_DUR 20 /* ms */ + +/* soft regulation */ +#define SW_REG_CURR_STEP_MA 100 +#define SW_REG_START_DELAY 500 +#define SW_REG_STEP_DELAY 100 + +struct max77693_charger_data { + struct max77693_dev *max77693; + + struct power_supply charger; + + /* mutex */ + struct mutex irq_lock; + struct mutex ops_lock; + + unsigned int charging_state; + unsigned int charging_type; + unsigned int battery_state; + unsigned int battery_present; + unsigned int cable_type; + unsigned int charging_current; + unsigned int vbus_state; + + int irq_bypass; + int irq_therm; + int irq_battery; + int irq_charge; + int irq_chargin; + + /* software regulation */ + bool soft_reg_state; + int soft_reg_current; + bool soft_reg_ing; + + /* unsufficient power */ + bool reg_loop_deted; + + struct max77693_charger_platform_data *charger_pdata; + + int irq; + u8 irq_reg; + int irq_cnt; +}; + +static int max77693_get_battery_present(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + u8 reg_data; + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_INT_OK, ®_data); + reg_data = ((reg_data & MAX77693_DETBAT) >> MAX77693_DETBAT_SHIFT); + + return !reg_data; +} + +static int max77693_get_vbus_state(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + int state; + u8 reg_data; + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_DETAILS_00, ®_data); + reg_data = ((reg_data & MAX77693_CHGIN_DTLS) >> + MAX77693_CHGIN_DTLS_SHIFT); + + switch (reg_data) { + case 0x00: + /* V chgin < UVLO */ + state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + case 0x01: + /* V chgin < V batt + minimum threshold */ + state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + case 0x02: + /* V chgin > OVLO */ + state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + case 0x03: + state = POWER_SUPPLY_HEALTH_GOOD; + break; + default: + state = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + chg_data->vbus_state = state; + return state; +} + +static int max77693_get_charger_type(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + int state; + u8 reg_data; + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_DETAILS_01, ®_data); + reg_data = ((reg_data & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT); + + switch (reg_data) { + case 0x0: + state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case 0x1: + case 0x2: + case 0x3: + state = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case 0x4 ... 0x8: + case 0xA: + case 0xB: + state = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + break; + } + + chg_data->charging_type = state; + return state; +} + +static int max77693_get_charger_state(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + int state; + u8 reg_data; + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_DETAILS_01, ®_data); + reg_data = ((reg_data & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT); + switch (reg_data) { + case 0x0: + case 0x1: + case 0x2: + /* + * Note that whether to consider 0x3 as CHARGING or FULL + * is arguable. + * According to TN's standard 0x3 (TOP-OFF) should be + * "FULL". + * According to the strict semantics of "FULL", this is + * "CHARGING". + */ + case 0x3: + state = POWER_SUPPLY_STATUS_CHARGING; + break; + case 0x4: + state = POWER_SUPPLY_STATUS_FULL; + break; + case 0x5: + case 0x6: + case 0x7: + state = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case 0x8: + case 0xA: + case 0xB: + state = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + state = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + + chg_data->charging_state = state; + return state; +} + +static int max77693_get_online(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + u8 reg_data; + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_INT_OK, ®_data); + return !!(reg_data & MAX77693_CHGIN_I); +} + +static int max77693_get_battery_health(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + int state; + bool low_bat = false; + u8 reg_data; + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_DETAILS_01, ®_data); + reg_data = ((reg_data & MAX77693_BAT_DTLS) >> MAX77693_BAT_DTLS_SHIFT); + switch (reg_data) { + case 0x00: /* NO BATT */ + state = POWER_SUPPLY_HEALTH_DEAD; + break; + case 0x01: /* V Batt < Prequalification */ + low_bat = true; + state = POWER_SUPPLY_HEALTH_GOOD; + break; + case 0x02: /* Takes too much time to charge. Damaged battery? */ + state = POWER_SUPPLY_HEALTH_DEAD; + break; + case 0x03: /* V Batt > Good > Prequal */ + state = POWER_SUPPLY_HEALTH_GOOD; + break; + case 0x04: /* Good > V Batt > Prequal. Not good enough */ + low_bat = true; + state = POWER_SUPPLY_HEALTH_GOOD; + break; + case 0x05: /* V Batt > Overvoltage */ + state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + case 0x06: /* I Batt > Overcurrent */ + state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + default: + state = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + if (state == POWER_SUPPLY_HEALTH_GOOD) + state = max77693_get_vbus_state(chg_data); + + /* Battery is healthy and fully-charged, but has low voltage? */ + if (state == POWER_SUPPLY_HEALTH_GOOD && low_bat && + max77693_get_charger_state(chg_data) == POWER_SUPPLY_STATUS_FULL) + state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + + chg_data->battery_state = state; + return state; +} + +static bool max77693_charger_unlock(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + u8 reg_data; + u8 chgprot; + int retry_cnt = 0; + bool need_init = false; + pr_debug("%s\n", __func__); + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_06, ®_data); + chgprot = ((reg_data & MAX77693_CHG_CHGPROT) >> + MAX77693_CHG_CHGPROT_SHIFT); + + if (chgprot == MAX77693_CHG_CHGPROT_UNLOCK) { + pr_debug("%s: unlocked state, return\n", __func__); + need_init = false; + goto unlock_finish; + } + + do { + max77693_write_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_06, + (MAX77693_CHG_CHGPROT_UNLOCK << + MAX77693_CHG_CHGPROT_SHIFT)); + + max77693_read_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_06, ®_data); + chgprot = ((reg_data & MAX77693_CHG_CHGPROT) >> + MAX77693_CHG_CHGPROT_SHIFT); + + if (chgprot != MAX77693_CHG_CHGPROT_UNLOCK) { + pr_err("%s: unlock err, chgprot(0x%x), retry(%d)\n", + __func__, chgprot, retry_cnt); + msleep(CHG_UNLOCK_DELAY); + } else { + pr_info("%s: unlock success, chgprot(0x%x)\n", + __func__, chgprot); + need_init = true; + break; + } + } while ((chgprot != MAX77693_CHG_CHGPROT_UNLOCK) && + (++retry_cnt < CHG_UNLOCK_RETRY)); + +unlock_finish: + return need_init; +} + +static void max77693_charger_reg_init(struct max77693_charger_data *chg_data) +{ + struct regmap *rmap = chg_data->max77693->regmap; + u8 reg_data; + + /* + * fast charge timer 10hrs + * restart threshold disable + * pre-qual charge enable(default) + */ + reg_data = (0x04 << 0) | (0x03 << 4); + max77693_write_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_01, reg_data); + + /* + * charge current 466mA(default) + * otg current limit 900mA + */ + reg_data = (1 << 7); + max77693_write_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_02, reg_data); + + /* + * top off current 100mA + * top off timer 0min + */ + reg_data = (0x00 << 0); /* 100mA */ + + reg_data |= (0x00 << 3); + max77693_write_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_03, reg_data); + + /* + * cv voltage 4.2V or 4.35V + * MINVSYS 3.6V(default) + */ + reg_data &= (~MAX77693_CHG_MINVSYS_MASK); + reg_data |= (MAX77693_CHG_MINVSYS_3_6V << MAX77693_CHG_MINVSYS_SHIFT); + reg_data &= (~MAX77693_CHG_CV_PRM_MASK); +#if defined(CONFIG_MACH_M0) + if ((system_rev != 3) && (system_rev >= 1)) + reg_data |= (MAX77693_CHG_CV_PRM_4_35V << 0); + else + reg_data |= (MAX77693_CHG_CV_PRM_4_20V << 0); +#else /* C1, C2, M3, T0, ... */ + reg_data |= (MAX77693_CHG_CV_PRM_4_35V << 0); +#endif + + /* + * For GC1 Model, MINVSYS is 3.4V. + * For GC1 Model PRMV( Primary Charge Regn. Voltage) = 4.2V. + * Actual expected regulated voltage needs to be 4.2V but due to + * internal resistance and circuit deviation we might have to set the + * benchmark a bit higher sometimes. (4.225V now) + */ +#if defined(CONFIG_MACH_GC1) + reg_data &= (~MAX77693_CHG_CV_PRM_MASK); + reg_data |= (0x17 << MAX77693_CHG_CV_PRM_SHIFT); + reg_data &= (~MAX77693_CHG_MINVSYS_MASK); + reg_data |= (0x4 << MAX77693_CHG_MINVSYS_SHIFT); +#endif + max77693_write_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_04, reg_data); + + /* VBYPSET 5V */ + reg_data = 0x50; + max77693_write_reg(rmap, MAX77693_CHG_REG_CHG_CNFG_11, reg_data); +} + +/* Support property from charger */ +static enum power_supply_property max77693_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static const char *model_name = "MAX77693"; +static const char *manufacturer = "Maxim Semiconductor"; +static int max77693_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max77693_charger_data *chg_data = container_of(psy, + struct max77693_charger_data, + charger); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = max77693_get_charger_state(chg_data); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = max77693_get_charger_type(chg_data); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = max77693_get_battery_health(chg_data); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = max77693_get_battery_present(chg_data); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = max77693_get_online(chg_data); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = model_name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = manufacturer; + break; + default: + return -EINVAL; + } + /* + * TODO: support chaging type... + val->intval = max77693_get_cable_type(chg_data); + */ + + return ret; +} + +static void max77693_charger_initialize(struct max77693_charger_data *chg_data) +{ + struct max77693_charger_platform_data *charger_pdata = + chg_data->charger_pdata; + struct regmap *rmap = chg_data->max77693->regmap; + int i; + + for (i = 0; i < charger_pdata->num_init_data; i++) + max77693_write_reg(rmap, charger_pdata->init_data[i].addr, + charger_pdata->init_data[i].data); +} + +static int max77693_charger_probe(struct platform_device *pdev) +{ + struct max77693_charger_data *chg_data; + struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); + struct max77693_platform_data *pdata = dev_get_platdata(max77693->dev); + int ret; + + pr_info("%s: charger init start\n", __func__); + + chg_data = devm_kzalloc(&pdev->dev, sizeof(struct max77693_charger_data), GFP_KERNEL); + if (!chg_data) + return -ENOMEM; + + platform_set_drvdata(pdev, chg_data); + chg_data->max77693 = max77693; + + mutex_init(&chg_data->irq_lock); + mutex_init(&chg_data->ops_lock); + + /* unlock charger setting protect */ + max77693_charger_unlock(chg_data); + + chg_data->charger_pdata = pdata->charger_data; + if (pdata->charger_data && pdata->charger_data->init_data) + max77693_charger_initialize(chg_data); + else + max77693_charger_reg_init(chg_data); + + chg_data->charger.name = "max77693-charger", + chg_data->charger.type = POWER_SUPPLY_TYPE_BATTERY, + chg_data->charger.properties = max77693_charger_props, + chg_data->charger.num_properties = ARRAY_SIZE(max77693_charger_props), + chg_data->charger.get_property = max77693_charger_get_property, + + ret = power_supply_register(&pdev->dev, &chg_data->charger); + if (ret) { + pr_err("%s: failed: power supply register\n", __func__); + goto err_kfree; + } + + return 0; + +err_kfree: + mutex_destroy(&chg_data->ops_lock); + mutex_destroy(&chg_data->irq_lock); + return ret; +} + +static int max77693_charger_remove(struct platform_device *pdev) +{ + struct max77693_charger_data *chg_data = platform_get_drvdata(pdev); + + mutex_destroy(&chg_data->ops_lock); + mutex_destroy(&chg_data->irq_lock); + + power_supply_unregister(&chg_data->charger); + + return 0; +} + +/* + * WORKAROUND: (test and remove w/ later MAX77693 chips) + * TODO: read chip revision and bypass this code if revision > ? + * Several interrupts occur while charging through TA. + * Suspended state cannot be maintained by the interrupts. + */ +static u8 saved_int_mask; +static int max77693_charger_suspend(struct device *dev) +{ + struct max77693_dev *max77693 = dev_get_drvdata(dev->parent); + u8 int_mask; + + /* Save the masking value */ + max77693_read_reg(max77693->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, + &saved_int_mask); + + /* Mask all the interrupts related to charger */ + int_mask = 0xff; + max77693_write_reg(max77693->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, + int_mask); + return 0; +} + +static int max77693_charger_resume(struct device *dev) +{ + struct max77693_dev *max77693 = dev_get_drvdata(dev->parent); + + /* Restore the saved masking value */ + max77693_write_reg(max77693->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, + saved_int_mask); + return 0; +} + +static SIMPLE_DEV_PM_OPS(max77693_charger_pm_ops, max77693_charger_suspend, + max77693_charger_resume); + +#ifdef CONFIG_OF +static struct of_device_id max77693_charger_of_match[] __initconst = { + { .compatible = "samsung,max77693-charger", }, + { }, +}; +#endif + +static struct platform_driver max77693_charger_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "max77693-charger", + .pm = &max77693_charger_pm_ops, + .of_match_table = of_match_ptr(max77693_charger_of_match), + }, + .probe = max77693_charger_probe, + .remove = max77693_charger_remove, +}; + +static int __init max77693_charger_init(void) +{ + return platform_driver_register(&max77693_charger_driver); +} + +static void __exit max77693_charger_exit(void) +{ + platform_driver_unregister(&max77693_charger_driver); +} + +module_init(max77693_charger_init); +module_exit(max77693_charger_exit); + +MODULE_AUTHOR("SangYoung Son "); +MODULE_DESCRIPTION("max77693 Charger driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index 77d8207..f42ae8a 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -54,6 +54,11 @@ struct max77693_reg_data { u8 data; }; +struct max77693_charger_platform_data { + struct max77693_reg_data *init_data; + int num_init_data; +}; + struct max77693_muic_platform_data { struct max77693_reg_data *init_data; int num_init_data; @@ -78,6 +83,9 @@ struct max77693_platform_data { struct max77693_regulator_data *regulators; int num_regulators; + /* charger data */ + struct max77693_charger_platform_data *charger_data; + /* muic data */ struct max77693_muic_platform_data *muic_data; }; diff --git a/include/linux/power/max77693_charger.h b/include/linux/power/max77693_charger.h new file mode 100644 index 0000000..0ad2f39 --- /dev/null +++ b/include/linux/power/max77693_charger.h @@ -0,0 +1,177 @@ +/* + * max77693_charger.h + * Samsung max77693 Charger Header + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * 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. + * + */ +#ifndef __MAX77693_CHARGER_H +#define __MAX77693_CHARGER_H __FILE__ +#include +#include +#include +#include + +#define ENABLE 1 +#define DISABLE 0 + +/* macro */ +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define ABS(x) ((x) < 0 ? (-1 * (x)) : (x)) +/* + * Use for battery + */ +#define OFF_CURR 0 /* charger off current */ +#define KEEP_CURR -1 /* keep previous current */ + +/* MAX77693_CHG_REG_CHG_INT */ +#define MAX77693_BYP_I (1 << 0) +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_BATP_I (1 << 2) +#else +#define MAX77693_THM_I (1 << 2) +#endif +#define MAX77693_BAT_I (1 << 3) +#define MAX77693_CHG_I (1 << 4) +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_WCIN_I (1 << 5) +#endif +#define MAX77693_CHGIN_I (1 << 6) + +/* MAX77693_CHG_REG_CHG_INT_MASK */ +#define MAX77693_BYP_IM (1 << 0) +#define MAX77693_THM_IM (1 << 2) +#define MAX77693_BAT_IM (1 << 3) +#define MAX77693_CHG_IM (1 << 4) +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_WCIN_IM (1 << 5) +#endif +#define MAX77693_CHGIN_IM (1 << 6) + +/* MAX77693_CHG_REG_CHG_INT_OK */ +#define MAX77693_BYP_OK 0x01 +#define MAX77693_BYP_OK_SHIFT 0 +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_BATP_OK 0x04 +#define MAX77693_BATP_OK_SHIFT 2 +#else +#define MAX77693_THM_OK 0x04 +#define MAX77693_THM_OK_SHIFT 2 +#endif +#define MAX77693_BAT_OK 0x08 +#define MAX77693_BAT_OK_SHIFT 3 +#define MAX77693_CHG_OK 0x10 +#define MAX77693_CHG_OK_SHIFT 4 +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_WCIN_OK 0x20 +#define MAX77693_WCIN_OK_SHIFT 5 +#endif +#define MAX77693_CHGIN_OK 0x40 +#define MAX77693_CHGIN_OK_SHIFT 6 +#define MAX77693_DETBAT 0x80 +#define MAX77693_DETBAT_SHIFT 7 + +/* MAX77693_CHG_REG_CHG_DTLS_00 */ +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_BATP_DTLS 0x01 +#define MAX77693_BATP_DTLS_SHIFT 0 +#else +#define MAX77693_THM_DTLS 0x07 +#define MAX77693_THM_DTLS_SHIFT 0 +#endif +#if defined(CONFIG_CHARGER_MAX77803) +#define MAX77693_WCIN_DTLS 0x18 +#define MAX77693_WCIN_DTLS_SHIFT 3 +#endif +#define MAX77693_CHGIN_DTLS 0x60 +#define MAX77693_CHGIN_DTLS_SHIFT 5 + +/* MAX77693_CHG_REG_CHG_DTLS_01 */ +#define MAX77693_CHG_DTLS 0x0F +#define MAX77693_CHG_DTLS_SHIFT 0 +#define MAX77693_BAT_DTLS 0x70 +#define MAX77693_BAT_DTLS_SHIFT 4 + +/* MAX77693_CHG_REG_CHG_DTLS_02 */ +#define MAX77693_BYP_DTLS 0x0F +#define MAX77693_BYP_DTLS_SHIFT 0 +#define MAX77693_BYP_DTLS0 0x1 +#define MAX77693_BYP_DTLS1 0x2 +#define MAX77693_BYP_DTLS2 0x4 +#define MAX77693_BYP_DTLS3 0x8 + +/* MAX77693_CHG_REG_CHG_CNFG_00 */ +#define MAX77693_MODE_DEFAULT 0x04 +#define MAX77693_MODE_CHGR 0x01 +#define MAX77693_MODE_OTG 0x02 +#define MAX77693_MODE_BUCK 0x04 + +/* MAX77693_CHG_REG_CHG_CNFG_02 */ +#define MAX77693_CHG_CC 0x3F + +/* MAX77693_CHG_REG_CHG_CNFG_03 */ +#define MAX77693_CHG_TO_ITH 0x07 + +/* MAX77693_CHG_REG_CHG_CNFG_04 */ +#define MAX77693_CHG_MINVSYS_MASK 0xE0 +#define MAX77693_CHG_MINVSYS_SHIFT 5 +#define MAX77693_CHG_PRM_MASK 0x1F +#define MAX77693_CHG_PRM_SHIFT 0 + +/* MAX77693_CHG_REG_CHG_CNFG_09 */ +#define MAX77693_CHG_CHGIN_LIM 0x7F + +/* MAX77693_CHG_REG_CHG_CNFG_12 */ +#define MAX77693_CHG_WCINSEL 0x40 + +/* MAX77693_MUIC_REG_CDETCTRL1 */ +#define MAX77693_CHGTYPMAN 0x02 +#define MAX77693_CHGTYPMAN_SHIFT 1 + +/* MAX77693_MUIC_REG_STATUS2 */ +#define MAX77693_VBVOLT 0x40 +#define MAX77693_VBVOLT_SHIFT 6 +#define MAX77693_CHGDETRUN 0x08 +#define MAX77693_CHGDETRUN_SHIFT 3 +#define MAX77693_CHGTYPE 0x07 +#define MAX77693_CHGTYPE_SHIFT 0 + +/* irq */ +#define IRQ_DEBOUNCE_TIME 20 /* msec */ + +/* charger type detection */ +#define DET_ERR_RETRY 5 +#define DET_ERR_DELAY 200 + +/* soft charging */ +#define SOFT_CHG_START_CURR 100 /* mA */ +#define SOFT_CHG_START_DUR 100 /* ms */ +#define SOFT_CHG_CURR_STEP 100 /* mA */ +#define SOFT_CHG_STEP_DUR 20 /* ms */ + +#define DEFAULT_AC_CURRENT 1600 /* mA */ +#define DEFAULT_USB_CURRENT 500 /* mA */ + +#ifndef CONFIG_MACH_SLP_ADONIS +enum { + POWER_SUPPLY_VBUS_UNKNOWN = 0, + POWER_SUPPLY_VBUS_UVLO, + POWER_SUPPLY_VBUS_WEAK, + POWER_SUPPLY_VBUS_OVLO, + POWER_SUPPLY_VBUS_GOOD, +}; +#endif + +extern sec_battery_platform_data_t sec_battery_pdata; +#endif -- 2.7.4