summaryrefslogtreecommitdiff
path: root/drivers/power/wmt_battery/charger
diff options
context:
space:
mode:
authorSrikant Patnaik2015-01-11 12:28:04 +0530
committerSrikant Patnaik2015-01-11 12:28:04 +0530
commit871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch)
tree8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/power/wmt_battery/charger
parent9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff)
downloadFOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized. Changes are basically to make it look like kernel structure.
Diffstat (limited to 'drivers/power/wmt_battery/charger')
-rwxr-xr-xdrivers/power/wmt_battery/charger/g2214/Kconfig5
-rwxr-xr-xdrivers/power/wmt_battery/charger/g2214/Makefile7
-rwxr-xr-xdrivers/power/wmt_battery/charger/g2214/g2214_charger.c762
-rwxr-xr-xdrivers/power/wmt_battery/charger/mp2625/Kconfig5
-rwxr-xr-xdrivers/power/wmt_battery/charger/mp2625/Makefile6
-rwxr-xr-xdrivers/power/wmt_battery/charger/mp2625/mp2625_charger.c428
6 files changed, 1213 insertions, 0 deletions
diff --git a/drivers/power/wmt_battery/charger/g2214/Kconfig b/drivers/power/wmt_battery/charger/g2214/Kconfig
new file mode 100755
index 00000000..1ca457f8
--- /dev/null
+++ b/drivers/power/wmt_battery/charger/g2214/Kconfig
@@ -0,0 +1,5 @@
+config G2214_CHARGER
+ tristate "GMT G2214 Charger driver (GMT & others)"
+ depends on I2C
+ help
+ Say Y here to enable support for charger with g2214 chip. \ No newline at end of file
diff --git a/drivers/power/wmt_battery/charger/g2214/Makefile b/drivers/power/wmt_battery/charger/g2214/Makefile
new file mode 100755
index 00000000..188872a7
--- /dev/null
+++ b/drivers/power/wmt_battery/charger/g2214/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for GMT charger core.
+#
+
+obj-y += g2214_charger.o
+
+
diff --git a/drivers/power/wmt_battery/charger/g2214/g2214_charger.c b/drivers/power/wmt_battery/charger/g2214/g2214_charger.c
new file mode 100755
index 00000000..944790e8
--- /dev/null
+++ b/drivers/power/wmt_battery/charger/g2214/g2214_charger.c
@@ -0,0 +1,762 @@
+/*
+ * g2214_charger.c - WonderMedia Charger Driver.
+ *
+ * Copyright (C) 2013 WonderMedia Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+#include <mach/wmt_env.h>
+#include <linux/power/wmt_battery.h>
+#include <mach/gmt-core.h>
+#include <linux/reboot.h>
+
+#define DRVNAME "gmt-charger"
+
+#undef pr_err
+#define pr_err(fmt, args...) printk("[" DRVNAME "] " fmt, ##args)
+
+#define REG_A0 0x00
+#define REG_A1 0x01
+#define REG_A2 0x02
+#define REG_A3 0x03
+#define REG_A4 0x04
+#define REG_A5 0x05
+#define REG_A6 0x06
+#define REG_A7 0x07
+#define REG_A8 0x08
+#define REG_A9 0x09
+#define REG_A10 0x0A
+#define REG_A11 0x0B
+#define REG_A12 0x0C
+#define REG_A13 0x0D
+
+struct g2214_charger {
+ struct gmt2214_dev *gmt_dev;
+ struct device *dev;
+ struct mutex lock;
+
+ struct power_supply psy_ac;
+ struct power_supply psy_usb;
+ struct delayed_work monitor_work;
+
+ int ac_online;
+ int usb_online;
+ int charger_status;
+ int sleeping;
+
+ union {
+ struct {
+ unsigned int cable_type:4;
+ unsigned int current_sw_mode:1;
+ unsigned int pc_charging:1;
+ };
+ uint32_t flag;
+ };
+
+ int iset_dcin;
+ int iset_vbus;
+ int vseta;
+ int iseta_small;
+ int iseta_large;
+ int safety_time;
+ int otg_power;
+ int pc_power_mode;
+};
+
+static struct g2214_charger *g_charger = NULL;
+
+static int parse_charger_param(struct g2214_charger *ch)
+{
+ static const char uboot_env[] = "wmt.charger.param";
+ char buf[64];
+ size_t l = sizeof(buf);
+ int n;
+
+ if (wmt_getsyspara((char *)uboot_env, buf, &l))
+ return -ENODEV;
+ if (prefixcmp(buf, "g2214:"))
+ return -ENODEV;
+ if (!ch)
+ return 0;
+
+ n = sscanf(buf + 6, "%x:%d:%d:%d:%d:%d:%d:%d:%d",
+ &ch->flag,
+ &ch->iset_dcin, &ch->iset_vbus, &ch->vseta,
+ &ch->iseta_small, &ch->iseta_large,
+ &ch->safety_time, &ch->otg_power, &ch->pc_power_mode);
+ if (n < 8) {
+ pr_err("%s invalid\n", uboot_env);
+ return -EINVAL;
+ }
+
+ pr_info("charger match g2214, %s cable, %s current switch\n"
+ "PC connected is %scharging\n"
+ "dcin %d mA, vbus %d mA, %d mV\n"
+ "charging current %d~%d mA %d hour, %s otg power\n"
+ "pc power in %s mode\n",
+ (ch->cable_type == CABLE_TYPE_DC) ? "DC" : "USB",
+ (ch->current_sw_mode == CURRENT_SWITCH_DYNAMIC) ? "dynamic" : "sleep",
+ (ch->pc_charging == PC_CONNECTED_NOT_CHARGING) ? "not " : "",
+ ch->iset_dcin, ch->iset_vbus, ch->vseta,
+ ch->iseta_small, ch->iseta_large,
+ ch->safety_time, ch->otg_power ? "switch" : "no",
+ ch->pc_power_mode ? "lowpower" : "normal");
+ return 0;
+}
+
+static int g2214_read(struct g2214_charger *ch, uint8_t reg)
+{
+ unsigned int rt_value = 0;
+ gmt2214_reg_read(ch->gmt_dev, reg, &rt_value);
+ return rt_value;
+}
+
+static int g2214_write(struct g2214_charger *ch, uint8_t reg, uint8_t val)
+{
+ return gmt2214_reg_write(ch->gmt_dev, reg, val);
+}
+
+static inline void g2214_enotg_config(struct g2214_charger *ch, int enable)
+{
+ int val = g2214_read(ch, REG_A8);
+ if (enable)
+ val |= BIT3;
+ else
+ val &= ~BIT3;
+ g2214_write(ch, REG_A8, val);
+}
+
+static inline void g2214_vseta_config(struct g2214_charger *ch)
+{
+ int val, vseta;
+
+ if (ch->vseta < 4150)
+ vseta = 0;
+ else if (ch->vseta < 4200)
+ vseta = 1;
+ else if (ch->vseta < 4350)
+ vseta = 2;
+ else
+ vseta = 3;
+
+ val = g2214_read(ch, REG_A8);
+ val &= ~(3 << 6);
+ val |= vseta << 6;
+ g2214_write(ch, REG_A8, val);
+}
+
+static inline void g2214_current_config(struct g2214_charger *ch,
+ int dcin_mA, int vbus_mA, int charge_mA)
+{
+ int iset_dcin, iset_vbus, iseta;
+
+ if (dcin_mA <= 1000)
+ iset_dcin = 0;
+ else if (dcin_mA <= 1500)
+ iset_dcin = 1;
+ else if (dcin_mA <= 2000)
+ iset_dcin = 2;
+ else
+ iset_dcin = 3;
+
+ if (vbus_mA <= 95)
+ iset_vbus = 0;
+ else if (vbus_mA <= 475)
+ iset_vbus = 1;
+ else if (vbus_mA <= 950)
+ iset_vbus = 2;
+ else
+ iset_vbus = 3;
+
+ if (charge_mA < 300 || charge_mA > 1800)
+ iseta = 2;
+ else
+ iseta = ((charge_mA - 300) / 100);
+
+ g2214_write(ch, REG_A5, iset_dcin << 6 | iset_vbus << 4 | iseta);
+}
+
+static void current_refresh(struct g2214_charger *ch)
+{
+ int dcin_mA, vbus_mA, charge_mA;
+
+ dcin_mA = ch->iset_dcin;
+
+ switch (ch->charger_status) {
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ vbus_mA = ch->pc_power_mode ? 95 : 475;
+ charge_mA = ch->iseta_small;
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ case POWER_SUPPLY_STATUS_CHARGING:
+ vbus_mA = ch->iset_vbus;
+ if (ch->current_sw_mode == CURRENT_SWITCH_DYNAMIC) {
+ charge_mA = ch->iseta_large;
+ } else {
+ charge_mA = ch->sleeping ? ch->iseta_large
+ : ch->iseta_small;
+ }
+ if (ch->cable_type == CABLE_TYPE_USB && wmt_is_pc_connected()) {
+ vbus_mA = 475;
+ charge_mA = ch->iseta_small;
+ }
+ break;
+ default:
+ return;
+ }
+
+ printk(KERN_DEBUG " ## %s: dcin_mA %d, vbus_mA %d, charge_mA %d\n",
+ __func__, dcin_mA, vbus_mA, charge_mA);
+ g2214_current_config(ch, dcin_mA, vbus_mA, charge_mA);
+ g2214_vseta_config(ch);
+}
+
+static void g2214_endpm_config(struct g2214_charger *ch, int en)
+{
+ int val = g2214_read(ch, REG_A0);
+ if (en)
+ val |= BIT3;
+ else
+ val &= ~BIT3;
+ g2214_write(ch, REG_A0, val);
+}
+
+static void g2214_safety_time_init(struct g2214_charger *ch)
+{
+ int val;
+ int safety_time = ch->safety_time - 1;
+
+ if (safety_time < 0)
+ safety_time = 0;
+ else if (safety_time > 16)
+ safety_time = 15;
+
+ val = g2214_read(ch, REG_A6);
+ val &= (~(BIT4 | BIT5 | BIT6 | BIT7));
+ val |= (safety_time << 4);
+ g2214_write(ch, REG_A6, val);
+}
+
+static void g2214_ntc_init(struct g2214_charger *ch)
+{
+ int val;
+ val = g2214_read(ch, REG_A0);
+ val &= ~BIT1; //Enable Auto NTC-R Type Detection
+ g2214_write(ch, REG_A0, val);
+ g2214_write(ch, REG_A7, 0); //Set HOT boundary to 60
+}
+
+static int g2214_reg_init(struct g2214_charger *ch)
+{
+ g2214_safety_time_init(ch);
+ g2214_enotg_config(ch, 0);
+ g2214_current_config(ch, ch->iset_dcin, ch->iset_vbus, ch->iseta_small);
+ g2214_endpm_config(ch, ch->current_sw_mode == CURRENT_SWITCH_DYNAMIC);
+ g2214_ntc_init(ch);
+ return 0;
+}
+
+static void g2214_regs_dump(struct g2214_charger *ch)
+{
+ int reg;
+ for (reg = REG_A0; reg <= REG_A13; reg++)
+ printk(KERN_DEBUG " ## reg A%d: 0x%x\n ",
+ reg, g2214_read(ch, reg));
+}
+
+static void g2214_charge_enable(struct g2214_charger *ch, int en)
+{
+ int val = g2214_read(ch, REG_A6);
+ if (en)
+ val &= ~BIT3;
+ else
+ val |= BIT3;
+ g2214_write(ch, REG_A6, val);
+}
+
+static inline int g2214_is_full(struct g2214_charger *ch)
+{
+ return !!(g2214_read(ch, REG_A12) & 0x10);
+}
+
+static int g2214_read_status(struct g2214_charger *ch, int *pst)
+{
+ if (!wmt_is_dc_plugin()) {
+ *pst = POWER_SUPPLY_STATUS_DISCHARGING;
+ return 0;
+ }
+
+ switch (ch->cable_type) {
+ case CABLE_TYPE_DC:
+ *pst = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case CABLE_TYPE_USB:
+ if (wmt_is_otg_plugin()) {
+ *pst = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else if (wmt_is_pc_connected()) {
+ if (ch->pc_charging == PC_CONNECTED_CHARGING)
+ *pst = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ *pst = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ *pst = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (*pst == POWER_SUPPLY_STATUS_CHARGING) {
+ if (g2214_is_full(ch))
+ *pst = POWER_SUPPLY_STATUS_FULL;
+ }
+ return 0;
+}
+
+static void charger_monitor_work(struct work_struct *work)
+{
+ struct g2214_charger *ch=
+ container_of(work, struct g2214_charger, monitor_work.work);
+ int ac_online = 0;
+ int usb_online = 0;
+ int charger_status;
+ int ts_meter = 0;
+
+ g2214_regs_dump(ch);
+
+ if (wmt_is_otg_plugin() && ch->otg_power)
+ g2214_enotg_config(ch, 1);
+ else {
+ g2214_enotg_config(ch, 0);
+ msleep(30);
+ }
+
+ g2214_write(ch, REG_A9, 0xFF);
+
+ g2214_read_status(ch, &charger_status);
+
+ ts_meter = (g2214_read(ch, REG_A10) & 0xE0) >> 5;
+ if (ts_meter == 0x11) {
+ charger_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ g2214_charge_enable(ch, 0);
+ printk("Battery Overheat, Charge Disable\n");
+ } else if (ts_meter <= 0)
+ g2214_charge_enable(ch, 1);
+
+ if (charger_status == POWER_SUPPLY_STATUS_CHARGING ||
+ charger_status == POWER_SUPPLY_STATUS_FULL) {
+ if (ch->cable_type == CABLE_TYPE_USB &&
+ ch->pc_charging == PC_CONNECTED_CHARGING &&
+ wmt_is_pc_connected())
+ usb_online = 1;
+ else
+ ac_online = 1;
+ }
+
+ if (ch->charger_status != charger_status ||
+ ch->ac_online != ac_online) {
+ ch->charger_status = charger_status;
+ ch->ac_online = ac_online;
+ power_supply_changed(&ch->psy_ac);
+ }
+
+ if (ch->pc_charging == PC_CONNECTED_CHARGING &&
+ ch->usb_online != usb_online) {
+ ch->usb_online = usb_online;
+ power_supply_changed(&ch->psy_usb);
+ }
+
+ current_refresh(ch);
+ led_power_enable(charger_status == POWER_SUPPLY_STATUS_CHARGING ||
+ charger_status == POWER_SUPPLY_STATUS_FULL);
+
+ g2214_write(ch, REG_A9, 0xF0);
+ g2214_write(ch, REG_A12, g2214_read(ch, REG_A12) & (~BIT0));
+}
+
+void g2214_pc_connected(void)
+{
+ if (g_charger)
+ schedule_delayed_work(&g_charger->monitor_work, 1.5*HZ);
+}
+
+static irqreturn_t otg_irq(int irq, void *data)
+{
+ struct g2214_charger *ch = data;
+
+ if (REG8_VAL(USB_BASE_ADD + 0x7F1) & BIT7) {
+ REG8_VAL(USB_BASE_ADD + 0x7F1) = BIT7;
+ schedule_delayed_work(&ch->monitor_work, 0);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+// g2214 + dcdet use wakeup0 interrupt
+static irqreturn_t g2214_dcdet_irq(int irq, void *data)
+{
+ struct g2214_charger *ch = data;
+
+ // turn off the led immediately
+ if (!wmt_is_dc_plugin())
+ led_power_enable(0);
+
+ if (PMCIS_VAL & BIT0) {
+ pmc_clear_intr_status(WKS_WK0);
+ schedule_delayed_work(&ch->monitor_work, 0);
+ return IRQ_HANDLED;
+ }
+
+ if (PMCIS_VAL & BIT27) {
+ pmc_clear_intr_status(WKS_DCDET);
+ schedule_delayed_work(&ch->monitor_work, 1.5*HZ);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static inline void pid_check_irq_enable(void)
+{
+ REG8_VAL(USB_BASE_ADD+0x7F2) |= BIT1;
+}
+
+static inline void pid_check_irq_disable(void)
+{
+ REG8_VAL(USB_BASE_ADD+0x7F2) &= ~BIT1;
+}
+
+static int irqs_init(struct g2214_charger *ch)
+{
+ unsigned long iflag = IRQF_SHARED;
+ int ret;
+
+ ret = devm_request_irq(ch->dev, IRQ_UHDC, otg_irq, iflag, "USBOTG", ch);
+ if (ret < 0) {
+ dev_err(ch->dev, "IRQ_UHDC irq request failed %d\n", ret);
+ return ret;
+ }
+
+ if (REG32_VAL(0xfe120000) == 0x35100101)
+ iflag |= IRQF_NO_SUSPEND;
+
+ ret = devm_request_irq(ch->dev, IRQ_PMC_WAKEUP, g2214_dcdet_irq, iflag,
+ "G2214-DCDET", ch);
+ if (ret < 0) {
+ pr_err("register DCDET irq failed\n");
+ return ret;
+ }
+
+ pid_check_irq_enable();
+ pmc_enable_wakeup_isr(WKS_WK0, 2);
+ wmt_dcdet_irq_enable();
+ return 0;
+}
+
+static void irqs_release(struct g2214_charger *ch)
+{
+ pid_check_irq_disable();
+ wmt_dcdet_irq_disable();
+ pmc_disable_wakeup_isr(WKS_WK0);
+}
+
+static enum power_supply_property ac_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+};
+
+static int ac_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct g2214_charger *ch =
+ container_of(psy, struct g2214_charger, psy_ac);
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ch->ac_online;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ return g2214_read_status(ch, &val->intval);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct g2214_charger *ch =
+ container_of(psy, struct g2214_charger, psy_usb);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ch->usb_online;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property usb_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *power_supplied_to[] = {
+ "battery",
+};
+
+static int power_supply_init(struct g2214_charger *ch)
+{
+ int ret;
+
+ ch->psy_ac.name = "ac",
+ ch->psy_ac.type = POWER_SUPPLY_TYPE_MAINS,
+ ch->psy_ac.supplied_to = power_supplied_to,
+ ch->psy_ac.num_supplicants = ARRAY_SIZE(power_supplied_to),
+ ch->psy_ac.properties = ac_properties,
+ ch->psy_ac.num_properties = ARRAY_SIZE(ac_properties),
+ ch->psy_ac.get_property = ac_get_property,
+ ret = power_supply_register(ch->dev, &ch->psy_ac);
+ if (ret) {
+ dev_err(ch->dev, "register ac power supply failed.\n");
+ }
+
+ if (ch->pc_charging == PC_CONNECTED_CHARGING) {
+ ch->psy_usb.name = "usb",
+ ch->psy_usb.type = POWER_SUPPLY_TYPE_USB,
+ ch->psy_usb.supplied_to = power_supplied_to,
+ ch->psy_usb.num_supplicants = ARRAY_SIZE(power_supplied_to),
+ ch->psy_usb.properties = usb_properties,
+ ch->psy_usb.num_properties = ARRAY_SIZE(usb_properties),
+ ch->psy_usb.get_property = usb_get_property,
+ ret = power_supply_register(ch->dev, &ch->psy_usb);
+ if (ret) {
+ dev_err(ch->dev, "register ac power supply failed.\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static void power_supply_release(struct g2214_charger *ch)
+{
+ power_supply_unregister(&ch->psy_ac);
+}
+
+#define G2214_PROC_NAME "driver/g2214_regs"
+
+static int g2214_proc_show(struct seq_file *seq, void *offset)
+{
+ int reg;
+ for (reg = REG_A0; reg <= REG_A13; reg++)
+ seq_printf(seq, "reg A%d: 0x%x\n ",
+ reg, g2214_read(g_charger, reg));
+ return 0;
+}
+
+static int g2214_proc_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ ret = single_open(file, g2214_proc_show, NULL);
+ if (ret)
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static ssize_t g2214_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+ unsigned long len = count;
+ int reg, val;
+
+ if (len > sizeof(cmd))
+ len = sizeof(cmd);
+
+ if (copy_from_user(cmd, buffer, len))
+ return -EFAULT;
+ if (sscanf(cmd, "r%d=0x%02x", &reg, &val) != 2)
+ return -EINVAL;
+ if (reg > REG_A13)
+ return -EINVAL;
+
+ if (g_charger) {
+ g2214_write(g_charger, reg, val);
+ pr_info("## %s: reg %d -> 0x%02x\n", __func__, reg, val);
+ }
+
+ return count;
+}
+
+static int g2214_proc_release(struct inode *inode, struct file *file)
+{
+ int res = single_release(inode, file);
+ module_put(THIS_MODULE);
+ return res;
+}
+
+static const struct file_operations g2214_proc_fops = {
+ .open = g2214_proc_open,
+ .read = seq_read,
+ .write = g2214_proc_write,
+ .llseek = seq_lseek,
+ .release = g2214_proc_release,
+};
+
+static inline void g2214_proc_setup(void)
+{
+ proc_create(G2214_PROC_NAME, 0, NULL, &g2214_proc_fops);
+}
+
+static inline void g2214_proc_cleanup(void)
+{
+ remove_proc_entry(G2214_PROC_NAME, NULL);
+}
+
+static int g2214_reboot_notifie(struct notifier_block *nb, unsigned long event, void *unused)
+{
+ struct g2214_charger *ch = g_charger;
+ cancel_delayed_work_sync(&ch->monitor_work);
+ g2214_enotg_config(ch, 0);
+ g2214_write(ch, REG_A9, 0xFF);
+ g2214_write(ch, REG_A11, 0xFF);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block g2214_reboot_notifier = {
+ .notifier_call = g2214_reboot_notifie,
+};
+
+
+static int __devinit g2214_probe(struct platform_device *pdev)
+{
+ struct g2214_charger *ch;
+ int ret;
+
+ ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
+ if (!ch)
+ return -ENOMEM;
+
+ if ((ret = parse_charger_param(ch)))
+ return ret;
+ parse_charger_led();
+
+ ch->dev = &pdev->dev;
+ ch->gmt_dev = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, ch);
+
+ if ((ret = power_supply_init(ch)))
+ return ret;
+
+ INIT_DELAYED_WORK(&ch->monitor_work, charger_monitor_work);
+
+ if ((ret = irqs_init(ch))) {
+ power_supply_release(ch);
+ return ret;
+ }
+
+ g_charger = ch;
+ g2214_reg_init(ch);
+ g2214_proc_setup();
+
+ register_reboot_notifier(&g2214_reboot_notifier);
+ schedule_delayed_work(&ch->monitor_work, 0);
+ pr_info(DRVNAME " install success.\n");
+ return 0;
+}
+
+static int __devexit g2214_remove(struct platform_device *pdev)
+{
+ struct g2214_charger *ch = platform_get_drvdata(pdev);
+ irqs_release(ch);
+ cancel_delayed_work_sync(&ch->monitor_work);
+ power_supply_release(ch);
+ g2214_proc_cleanup();
+ g_charger = NULL;
+ return 0;
+}
+
+static int g2214_suspend(struct device *dev)
+{
+ struct g2214_charger *ch = dev_get_drvdata(dev);
+ cancel_delayed_work_sync(&ch->monitor_work);
+ ch->sleeping = 1;
+ current_refresh(ch);
+ g2214_write(ch, REG_A11, 0xCF);
+ return 0;
+}
+
+static int g2214_resume(struct device *dev)
+{
+ struct g2214_charger *ch = dev_get_drvdata(dev);
+
+ // turn off the led immediately
+ if (!wmt_is_dc_plugin())
+ led_power_enable(0);
+
+ pid_check_irq_enable();
+ ch->sleeping = 0;
+ current_refresh(ch);
+ schedule_delayed_work(&ch->monitor_work, HZ);
+ g2214_write(ch, REG_A11, 0xFF);
+ return 0;
+}
+
+static const struct dev_pm_ops g2214_pm_ops = {
+ .suspend = g2214_suspend,
+ .resume = g2214_resume,
+};
+
+static struct platform_driver g2214_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ .pm = &g2214_pm_ops,
+ },
+ .probe = g2214_probe,
+ .remove = __devexit_p(g2214_remove),
+};
+
+static int __init g2214_init(void)
+{
+ if (parse_charger_param(NULL))
+ return -ENODEV;
+ return platform_driver_register(&g2214_driver);
+}
+
+static void __exit g2214_exit(void)
+{
+ return platform_driver_unregister(&g2214_driver);
+}
+
+module_init(g2214_init);
+module_exit(g2214_exit);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("GMT2144 battery charger driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:g2214");
+
diff --git a/drivers/power/wmt_battery/charger/mp2625/Kconfig b/drivers/power/wmt_battery/charger/mp2625/Kconfig
new file mode 100755
index 00000000..38282c65
--- /dev/null
+++ b/drivers/power/wmt_battery/charger/mp2625/Kconfig
@@ -0,0 +1,5 @@
+config MP2625_CHARGER
+ tristate "MP2625 Charger driver"
+ depends on I2C
+ help
+ Say Y here to enable support for charger with mp2625 chip.
diff --git a/drivers/power/wmt_battery/charger/mp2625/Makefile b/drivers/power/wmt_battery/charger/mp2625/Makefile
new file mode 100755
index 00000000..9bcfa83d
--- /dev/null
+++ b/drivers/power/wmt_battery/charger/mp2625/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for MP2625 charger core.
+#
+
+obj-y += mp2625_charger.o
+
diff --git a/drivers/power/wmt_battery/charger/mp2625/mp2625_charger.c b/drivers/power/wmt_battery/charger/mp2625/mp2625_charger.c
new file mode 100755
index 00000000..581e89d8
--- /dev/null
+++ b/drivers/power/wmt_battery/charger/mp2625/mp2625_charger.c
@@ -0,0 +1,428 @@
+/*
+ * mp2625_charger.c - WonderMedia Charger Driver.
+ *
+ * Copyright (C) 2013 WonderMedia Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <mach/wmt_env.h>
+#include <mach/hardware.h>
+#include <mach/wmt_iomux.h>
+#include <linux/gpio.h>
+#include <linux/power/wmt_battery.h>
+
+#define DRVNAME "mp2625-charger"
+
+#undef pr_err
+#undef pr_info
+#define pr_err(fmt, args...) printk("[" DRVNAME "] " fmt, ##args)
+#define pr_info(fmt, args...) printk("[" DRVNAME "] " fmt, ##args)
+
+struct mp2625_charger {
+ struct device *dev;
+ int ac_online;
+ int usb_online;
+ int charger_status;
+ int sleeping;
+
+ union {
+ struct {
+ unsigned int cable_type:4;
+ unsigned int current_sw_mode:1;
+ unsigned int pc_charging:1;
+ };
+ uint32_t flag;
+ };
+
+ int full_pin;
+ int full_level;
+ int current_pin;
+ int current_large_level;
+
+ struct power_supply psy_ac;
+ struct power_supply psy_usb;
+ struct delayed_work dwork;
+};
+
+static struct mp2625_charger *g_charger;
+
+static int parse_charger_param(struct mp2625_charger *ch)
+{
+ static char uboot_env[] = "wmt.charger.param";
+ char buf[64];
+ size_t l = sizeof(buf);
+ int ret;
+
+ if (wmt_getsyspara(uboot_env, buf, &l))
+ return -ENODEV;
+ if (prefixcmp(buf, "mp2625:"))
+ return -ENODEV;
+ if (!ch)
+ return 0;
+
+ ret = sscanf(buf + 7, "%x:%d:%d:%d:%d",
+ &ch->flag,
+ &ch->full_pin, &ch->full_level,
+ &ch->current_pin, &ch->current_large_level);
+ if (ret < 5) {
+ pr_err("Invalid uboot env: %s\n", uboot_env);
+ return -EINVAL;
+ }
+
+ if (ch->cable_type != CABLE_TYPE_DC &&
+ ch->cable_type != CABLE_TYPE_USB) {
+ pr_err("Invalid type %d\n", ch->cable_type);
+ return -EINVAL;
+ }
+
+ if (gpio_is_valid(ch->full_pin)) {
+ ret = devm_gpio_request(ch->dev, ch->full_pin, "charger full");
+ if (ret) {
+ pr_err("gpio%d request fail %d\n", ch->full_pin, ret);
+ return ret;
+ }
+ wmt_gpio_setpull(ch->full_pin, (ch->full_level) ?
+ WMT_GPIO_PULL_DOWN : WMT_GPIO_PULL_UP);
+ gpio_direction_input(ch->full_pin);
+ }
+
+ if (gpio_is_valid(ch->current_pin)) {
+ ret = devm_gpio_request(ch->dev, ch->current_pin, "charger current");
+ if (ret) {
+ pr_err("gpio%d request fail %d\n", ch->current_pin, ret);
+ return ret;
+ }
+ gpio_direction_output(ch->current_pin, !ch->current_large_level);
+ }
+
+ pr_info("charger match " DRVNAME ", %s cable, full %d, current %d\n"
+ "%s current switch, PC connected is %scharging\n",
+ (ch->cable_type == CABLE_TYPE_DC) ? "DC" : "USB",
+ ch->full_pin, ch->current_pin,
+ (ch->current_sw_mode == CURRENT_SWITCH_DYNAMIC) ? "dynamic" : "sleep",
+ (ch->pc_charging == PC_CONNECTED_NOT_CHARGING) ? "not " : "");
+ return 0;
+}
+
+static inline void set_current(struct mp2625_charger *ch)
+{
+ int large;
+ int charging = (ch->charger_status == POWER_SUPPLY_STATUS_CHARGING ||
+ ch->charger_status == POWER_SUPPLY_STATUS_FULL);
+
+ if (ch->current_sw_mode == CURRENT_SWITCH_DYNAMIC)
+ large = charging;
+ else
+ large = ch->sleeping ? 1 : 0;
+
+ if (ch->cable_type == CABLE_TYPE_USB && wmt_is_pc_connected())
+ large = 0;
+
+ if (gpio_is_valid(ch->current_pin)) {
+ gpio_direction_output(ch->current_pin,
+ large ? ch->current_large_level :
+ !ch->current_large_level);
+ printk(KERN_DEBUG "set %s current\n", large ? "large" : "small");
+ }
+}
+
+static int ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct mp2625_charger *ch =
+ container_of(psy, struct mp2625_charger, psy_ac);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ch->ac_online;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = ch->charger_status;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property ac_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+};
+
+static int usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct mp2625_charger *ch =
+ container_of(psy, struct mp2625_charger, psy_usb);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ch->usb_online;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property usb_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *power_supplied_to[] = {
+ "battery",
+};
+
+static int power_supply_init(struct mp2625_charger *ch)
+{
+ int ret;
+
+ ch->psy_ac.name = "ac",
+ ch->psy_ac.type = POWER_SUPPLY_TYPE_MAINS,
+ ch->psy_ac.supplied_to = power_supplied_to,
+ ch->psy_ac.num_supplicants = ARRAY_SIZE(power_supplied_to),
+ ch->psy_ac.properties = ac_properties,
+ ch->psy_ac.num_properties = ARRAY_SIZE(ac_properties),
+ ch->psy_ac.get_property = ac_get_property,
+ ret = power_supply_register(ch->dev, &ch->psy_ac);
+ if (ret) {
+ dev_err(ch->dev, "register ac power supply failed.\n");
+ return ret;
+ }
+
+ if (ch->pc_charging == PC_CONNECTED_CHARGING) {
+ ch->psy_usb.name = "usb",
+ ch->psy_usb.type = POWER_SUPPLY_TYPE_USB,
+ ch->psy_usb.supplied_to = power_supplied_to,
+ ch->psy_usb.num_supplicants = ARRAY_SIZE(power_supplied_to),
+ ch->psy_usb.properties = usb_properties,
+ ch->psy_usb.num_properties = ARRAY_SIZE(usb_properties),
+ ch->psy_usb.get_property = usb_get_property,
+ ret = power_supply_register(ch->dev, &ch->psy_usb);
+ if (ret) {
+ dev_err(ch->dev, "register ac power supply failed.\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void power_supply_release(struct mp2625_charger *ch)
+{
+ power_supply_unregister(&ch->psy_ac);
+ if (ch->pc_charging == PC_CONNECTED_CHARGING)
+ power_supply_unregister(&ch->psy_usb);
+}
+
+static void mp2625_charger_work(struct work_struct *work)
+{
+ struct mp2625_charger *ch =
+ container_of(work, struct mp2625_charger, dwork.work);
+ int ac_online = 0;
+ int usb_online = 0;
+ int charger_status = 0;
+
+ if (wmt_is_dc_plugin()) {
+ if (ch->cable_type == CABLE_TYPE_USB && wmt_is_pc_connected()) {
+ if (ch->pc_charging == PC_CONNECTED_CHARGING) {
+ charger_status = POWER_SUPPLY_STATUS_CHARGING;
+ usb_online = 1;
+ } else
+ charger_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else {
+ charger_status = POWER_SUPPLY_STATUS_CHARGING;
+ ac_online = 1;
+ }
+
+ if (charger_status == POWER_SUPPLY_STATUS_CHARGING &&
+ gpio_get_value(ch->full_pin) == ch->full_level)
+ charger_status = POWER_SUPPLY_STATUS_FULL;
+ } else
+ charger_status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ if (ch->ac_online != ac_online ||
+ ch->charger_status != charger_status) {
+ ch->ac_online = ac_online;
+ ch->charger_status = charger_status;
+ power_supply_changed(&ch->psy_ac);
+ }
+
+ if (ch->pc_charging == PC_CONNECTED_CHARGING &&
+ ch->usb_online != usb_online) {
+ ch->usb_online = usb_online;
+ power_supply_changed(&ch->psy_usb);
+ }
+
+ set_current(ch);
+ led_power_enable(charger_status == POWER_SUPPLY_STATUS_CHARGING ||
+ charger_status == POWER_SUPPLY_STATUS_FULL);
+}
+
+void mp2625_pc_connected(void)
+{
+ if (g_charger)
+ schedule_delayed_work(&g_charger->dwork, 0);
+}
+
+static irqreturn_t dcdet_irq(int irq, void *data)
+{
+ struct mp2625_charger *ch = data;
+
+ if (PMCIS_VAL & BIT27) {
+ pmc_clear_intr_status(WKS_DCDET);
+ schedule_delayed_work(&ch->dwork, HZ/2);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int irqs_init(struct mp2625_charger *ch)
+{
+ unsigned long iflag = IRQF_SHARED;
+ int ret;
+
+ if (REG32_VAL(0xfe120000) == 0x35100101)
+ iflag |= IRQF_NO_SUSPEND;
+
+ ret = devm_request_irq(ch->dev, IRQ_PMC_WAKEUP, dcdet_irq,
+ iflag, "WMT-DCDET", ch);
+ if (ret < 0) {
+ pr_err("register DCDET irq failed\n");
+ return ret;
+ }
+
+ wmt_dcdet_irq_enable();
+ return 0;
+}
+
+static void irqs_release(struct mp2625_charger *ch)
+{
+ wmt_dcdet_irq_disable();
+}
+
+static int mp2625_probe(struct platform_device *pdev)
+{
+ struct mp2625_charger *ch;
+ int ret;
+
+ ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
+ if (!ch)
+ return -ENOMEM;
+
+ ch->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ch);
+
+ ret = parse_charger_param(ch);
+ if (ret)
+ return ret;;
+ parse_charger_led();
+
+ if ((ret = power_supply_init(ch)))
+ return ret;
+
+ INIT_DELAYED_WORK(&ch->dwork, mp2625_charger_work);
+
+ if ((ret = irqs_init(ch))) {
+ power_supply_release(ch);
+ return ret;
+ }
+
+ g_charger = ch;
+ schedule_delayed_work(&ch->dwork, 0);
+
+ pr_info(DRVNAME " install success.\n");
+ return 0;
+}
+
+static int __devexit mp2625_remove(struct platform_device *pdev)
+{
+ struct mp2625_charger *ch = platform_get_drvdata(pdev);
+ irqs_release(ch);
+ cancel_delayed_work_sync(&ch->dwork);
+ power_supply_release(ch);
+ g_charger = NULL;
+ return 0;
+}
+
+static int mp2625_suspend(struct device *dev)
+{
+ struct mp2625_charger *ch = dev_get_drvdata(dev);
+ cancel_delayed_work_sync(&ch->dwork);
+ ch->sleeping = 1;
+ set_current(ch);
+ return 0;
+}
+
+static int mp2625_resume(struct device *dev)
+{
+ struct mp2625_charger *ch = dev_get_drvdata(dev);
+ schedule_delayed_work(&ch->dwork, HZ/2);
+ ch->sleeping = 0;
+ set_current(ch);
+ return 0;
+}
+
+static const struct dev_pm_ops mp2625_pm_ops = {
+ .suspend = mp2625_suspend,
+ .resume = mp2625_resume,
+};
+
+static struct platform_driver mp2625_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ .pm = &mp2625_pm_ops,
+ },
+ .probe = mp2625_probe,
+ .remove = mp2625_remove,
+};
+
+static struct platform_device *pdev;
+
+static int __init mp2625_init(void)
+{
+ int ret;
+
+ ret = parse_charger_param(NULL);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&mp2625_driver);
+ if (ret)
+ return ret;
+
+ pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ platform_driver_unregister(&mp2625_driver);
+ }
+ return ret;
+}
+
+static void __exit mp2625_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&mp2625_driver);
+}
+
+module_init(mp2625_init);
+module_exit(mp2625_exit);
+
+MODULE_AUTHOR("WonderMedia");
+MODULE_DESCRIPTION("MP2625 Charger Driver");
+MODULE_LICENSE("GPL");
+