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"