power: add wirelesscharger(WLC) bq51051b driver
Change-Id: I61c4b9c3d648e8f709ec78708271f807ceed1715
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8489196..be58719 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -440,4 +440,17 @@
bool "LGE battery id checker"
help
LGE Battery Id Checker Configuration
+
+config WIRELESS_CHARGER
+ bool "Wireless Charger Configuration"
+ default n
+ help
+ wireless charger configuration
+
+config BQ51051B_CHARGER
+ bool "TI BQ51051B Wireless Charging control Driver"
+ depends on WIRELESS_CHARGER
+ help
+ TI BQ51051B wireless charging control driver
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 007d75b..f6e8a14 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -58,3 +58,4 @@
obj-$(CONFIG_PM8921_CHARGER) += pm8921-charger.o
obj-$(CONFIG_LTC4088_CHARGER) += ltc4088-charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
+obj-$(CONFIG_BQ51051B_CHARGER) += bq51051b_charger.o
diff --git a/drivers/power/bq51051b_charger.c b/drivers/power/bq51051b_charger.c
new file mode 100644
index 0000000..3e56793
--- /dev/null
+++ b/drivers/power/bq51051b_charger.c
@@ -0,0 +1,300 @@
+/*
+ * BQ51051B Wireless Charging(WLC) control driver
+ *
+ * Copyright (C) 2012 LG Electronics, Inc
+ *
+ * This package 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/pm8921-charger.h>
+#include <linux/mfd/pm8xxx/pm8921-bms.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/leds-pm8xxx.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/power_supply.h>
+
+#include <linux/power_supply.h>
+#include <linux/power/bq51051b_charger.h>
+
+struct bq51051b_wlc_chip {
+ struct device *dev;
+ struct power_supply wireless_psy;
+ struct work_struct wireless_interrupt_work;
+ struct wake_lock wireless_chip_wake_lock;
+ unsigned int active_n_gpio;
+};
+
+static const struct platform_device_id bq51051b_id[] = {
+ {BQ51051B_WLC_DEV_NAME, 0},
+ {},
+};
+
+static struct bq51051b_wlc_chip *the_chip;
+static bool wireless_charging;
+static bool wireless_charge_done;
+
+static void bms_notify(struct bq51051b_wlc_chip *chip, int value)
+{
+ if (value)
+ pm8921_bms_charging_began();
+ else
+ pm8921_bms_charging_end(1);
+}
+
+static enum power_supply_property pm_power_props_wireless[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pm_power_supplied_to[] = {
+ "battery",
+};
+
+static int pm_power_get_property_wireless(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ /* Check if called before init */
+ if (!the_chip)
+ return -EINVAL;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1; //always battery_on
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = wireless_charging;
+
+ WLC_DBG(KERN_INFO "[wireless_charging] = %d",
+ wireless_charging);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int wireless_is_plugged(struct bq51051b_wlc_chip *chip)
+{
+ return !(gpio_get_value(chip->active_n_gpio));
+}
+
+static void wireless_set(struct bq51051b_wlc_chip *chip)
+{
+ WLC_DBG_ERROR("wireless_set\n");
+
+ wake_lock(&chip->wireless_chip_wake_lock);
+
+ wireless_charging = true;
+ wireless_charge_done = false;
+
+ bms_notify(chip, 1);
+
+ power_supply_changed(&chip->wireless_psy);
+ set_wireless_power_supply_control(wireless_charging);
+}
+
+static void wireless_reset(struct bq51051b_wlc_chip *chip)
+{
+ WLC_DBG_ERROR("wireless_reset\n");
+
+ wireless_charging = false;
+ wireless_charge_done = false;
+
+ bms_notify(chip, 0);
+
+ power_supply_changed(&chip->wireless_psy);
+ set_wireless_power_supply_control(wireless_charging);
+
+ wake_unlock(&chip->wireless_chip_wake_lock);
+}
+
+static void wireless_interrupt_worker(struct work_struct *work)
+{
+ struct bq51051b_wlc_chip *chip =
+ container_of(work, struct bq51051b_wlc_chip,
+ wireless_interrupt_work);
+
+ if (wireless_is_plugged(chip))
+ wireless_set(chip);
+ else
+ wireless_reset(chip);
+}
+
+static irqreturn_t wireless_interrupt_handler(int irq, void *data)
+{
+ int chg_state;
+ struct bq51051b_wlc_chip *chip = data;
+
+ WLC_DBG_ERROR("=========== ACTIVE INT START ==============\n");
+ chg_state = wireless_is_plugged(chip);
+ WLC_DBG_ERROR("wireless is plugged state = %d\n", chg_state);
+ schedule_work(&chip->wireless_interrupt_work);
+ WLC_DBG_ERROR("========== ACTIVE INT END ===============\n");
+ return IRQ_HANDLED;
+}
+
+static int __devinit bq51051b_wlc_hw_init(struct bq51051b_wlc_chip *chip)
+{
+ int ret;
+ WLC_DBG_ERROR("hw_init");
+
+ /* active_n pin is the bq51051b status, High = no charging, Low = charging */
+ ret = gpio_request_one(chip->active_n_gpio, GPIOF_DIR_IN, "active_n_gpio");
+ if (ret < 0) {
+ WLC_DBG_ERROR("active_n gpio request failed");
+ goto active_n_error;
+ }
+
+ /* active_n pin must be monitoring the bq51051b status */
+ ret = request_irq(gpio_to_irq(chip->active_n_gpio),
+ wireless_interrupt_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "wireless_charger", chip);
+ if (ret < 0) {
+ WLC_DBG_ERROR("wireless_charger request irq failed\n");
+ goto active_n_irq_error;
+ }
+ enable_irq_wake(gpio_to_irq(chip->active_n_gpio));
+
+ return 0;
+
+active_n_irq_error:
+ gpio_free(chip->active_n_gpio);
+active_n_error:
+ return ret;
+}
+
+static int bq51051b_wlc_resume(struct device *dev)
+{
+ WLC_DBG_ERROR("resume\n");
+ return 0;
+}
+
+static int bq51051b_wlc_suspend(struct device *dev)
+{
+ WLC_DBG_ERROR("suspend\n");
+ return 0;
+}
+
+static int __devinit bq51051b_wlc_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct bq51051b_wlc_chip *chip;
+ const struct bq51051b_wlc_platform_data *pdata
+ = pdev->dev.platform_data;
+
+ WLC_DBG_ERROR("probe\n");
+
+ if (!pdata) {
+ WLC_DBG_ERROR("missing platform data\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(struct bq51051b_wlc_chip), GFP_KERNEL);
+ if (!chip) {
+ WLC_DBG_ERROR("Cannot allocate bq51051b_wlc_chip\n");
+ return -ENOMEM;
+ }
+
+ chip->dev = &pdev->dev;
+
+ chip->active_n_gpio = pdata->active_n_gpio;
+
+ rc = bq51051b_wlc_hw_init(chip);
+ if (rc) {
+ WLC_DBG_ERROR("couldn't init hardware rc = %d\n", rc);
+ goto free_chip;
+ }
+
+ chip->wireless_psy.name = "wireless";
+ chip->wireless_psy.type = POWER_SUPPLY_TYPE_WIRELESS;
+ chip->wireless_psy.supplied_to = pm_power_supplied_to;
+ chip->wireless_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+ chip->wireless_psy.properties = pm_power_props_wireless;
+ chip->wireless_psy.num_properties = ARRAY_SIZE(pm_power_props_wireless);
+ chip->wireless_psy.get_property = pm_power_get_property_wireless;
+
+ rc = power_supply_register(chip->dev, &chip->wireless_psy);
+ if (rc < 0) {
+ WLC_DBG_ERROR("power_supply_register wireless failed rx = %d\n",
+ rc);
+ goto free_chip;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ the_chip = chip;
+
+ INIT_WORK(&chip->wireless_interrupt_work, wireless_interrupt_worker);
+ wake_lock_init(&chip->wireless_chip_wake_lock, WAKE_LOCK_SUSPEND,
+ "bq51051b_wireless_chip");
+
+ /* For Booting Wireless_charging and For Power Charging Logo In Wireless Charging */
+ if (wireless_is_plugged(chip))
+ wireless_set(chip);
+
+ return 0;
+
+free_chip:
+ kfree(chip);
+ return rc;
+}
+
+static int __devexit bq51051b_wlc_remove(struct platform_device *pdev)
+{
+ struct bq51051b_wlc_chip *chip = platform_get_drvdata(pdev);
+
+ WLC_DBG_ERROR("remove\n");
+ wake_lock_destroy(&chip->wireless_chip_wake_lock);
+ the_chip = NULL;
+ platform_set_drvdata(pdev, NULL);
+ power_supply_unregister(&chip->wireless_psy);
+ free_irq(gpio_to_irq(chip->active_n_gpio), chip);
+ gpio_free(chip->active_n_gpio);
+ kfree(chip);
+ return 0;
+}
+
+static const struct dev_pm_ops bq51051b_pm_ops = {
+ .suspend = bq51051b_wlc_suspend,
+ .resume = bq51051b_wlc_resume,
+};
+
+static struct platform_driver bq51051b_wlc_driver = {
+ .probe = bq51051b_wlc_probe,
+ .remove = __devexit_p(bq51051b_wlc_remove),
+ .id_table = bq51051b_id,
+ .driver = {
+ .name = BQ51051B_WLC_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &bq51051b_pm_ops,
+ },
+};
+
+static int __init bq51051b_wlc_init(void)
+{
+ return platform_driver_register(&bq51051b_wlc_driver);
+}
+
+static void __exit bq51051b_wlc_exit(void)
+{
+ platform_driver_unregister(&bq51051b_wlc_driver);
+}
+
+late_initcall(bq51051b_wlc_init);
+module_exit(bq51051b_wlc_exit);
+
+MODULE_AUTHOR("Kyungtae Oh <Kyungtae.oh@lge.com>");
+MODULE_DESCRIPTION("BQ51051B Wireless Charger Control Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index e3e5cca..148b9ff 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -352,6 +352,25 @@
return ret.intval;
}
+#ifdef CONFIG_WIRELESS_CHARGER
+static int wireless_chg_plugged_in(void)
+{
+ union power_supply_propval ret = {0,};
+ static struct power_supply *psy;
+
+ if (psy == NULL) {
+ psy = power_supply_get_by_name("wireless");
+ if (psy == NULL)
+ return 0;
+ }
+
+ if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
+ return 0;
+
+ return ret.intval;
+}
+#endif
+
#define HOLD_OREG_DATA BIT(1)
static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
{
@@ -870,6 +889,10 @@
usb_chg = usb_chg_plugged_in();
+#ifdef CONFIG_WIRELESS_CHARGER
+ usb_chg |= wireless_chg_plugged_in();
+#endif
+
convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
*ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
@@ -901,6 +924,10 @@
mutex_unlock(&chip->bms_output_lock);
usb_chg = usb_chg_plugged_in();
+
+#ifdef CONFIG_WIRELESS_CHARGER
+ usb_chg |= wireless_chg_plugged_in();
+#endif
convert_vbatt_raw_to_uv(chip, usb_chg,
raw->vbatt_for_rbatt_raw, &raw->vbatt_for_rbatt_uv);
convert_vbatt_raw_to_uv(chip, usb_chg,
@@ -947,6 +974,10 @@
usb_chg = usb_chg_plugged_in();
+#ifdef CONFIG_WIRELESS_CHARGER
+ usb_chg |= wireless_chg_plugged_in();
+#endif
+
if (chip->prev_last_good_ocv_raw == 0) {
chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
adjust_pon_ocv_raw(chip, raw);
@@ -1692,6 +1723,10 @@
voltage = xoadc_reading_to_microvolt(result.adc_code);
usb_chg = usb_chg_plugged_in();
+
+#ifdef CONFIG_WIRELESS_CHARGER
+ usb_chg |= wireless_chg_plugged_in();
+#endif
pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
"usb_chg = %d\n",
result.adc_code, voltage, result.measurement,
@@ -2278,6 +2313,10 @@
ocv_uv = 0;
pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
usb_chg = usb_chg_plugged_in();
+
+#ifdef CONFIG_WIRELESS_CHARGER
+ usb_chg |= wireless_chg_plugged_in();
+#endif
rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
if (rc || ocv_uv == 0) {
rc = adc_based_ocv(chip, &ocv_uv);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 2f2da89..848e910 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -291,6 +291,10 @@
static struct pm8xxx_adc_arb_btm_param btm_config;
+#ifdef CONFIG_WIRELESS_CHARGER
+static int wireless_charging;
+#endif
+
static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr,
u8 mask, u8 val)
{
@@ -1551,6 +1555,12 @@
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
+#ifdef CONFIG_WIRELESS_CHARGER
+ if(wireless_charging) {
+ val->intval = 1; //POWER_SUPPLY_STATUS_CHARGING
+ break;
+ }
+#endif
val->intval = get_prop_batt_status(chip);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
@@ -1954,6 +1964,22 @@
}
EXPORT_SYMBOL_GPL(pm8921_set_usb_power_supply_type);
+#ifdef CONFIG_WIRELESS_CHARGER
+int set_wireless_power_supply_control(int value)
+{
+ if (!the_chip) {
+ pr_err("called before init\n");
+ return -EINVAL;
+ }
+
+ wireless_charging = value;
+ power_supply_changed(&the_chip->batt_psy);
+
+ return 0;
+}
+EXPORT_SYMBOL(set_wireless_power_supply_control);
+#endif
+
int pm8921_batt_temperature(void)
{
if (!the_chip) {
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 4368e7d..21f0808 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -45,7 +45,10 @@
char *buf) {
static char *type_text[] = {
"Unknown", "Battery", "UPS", "Mains", "USB",
- "USB_DCP", "USB_CDP", "USB_ACA"
+ "USB_DCP", "USB_CDP", "USB_ACA",
+#ifdef CONFIG_WIRELESS_CHARGER
+ "WIRELESS"
+#endif
};
static char *status_text[] = {
"Unknown", "Charging", "Discharging", "Not charging", "Full"