Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/power/pm8058_usb_fix.c b/drivers/power/pm8058_usb_fix.c
new file mode 100644
index 0000000..80b1f87
--- /dev/null
+++ b/drivers/power/pm8058_usb_fix.c
@@ -0,0 +1,357 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+/* Config Regs  and their bits*/
+#define PM8058_CHG_TEST			0x75
+#define IGNORE_LL                       2
+
+#define PM8058_CHG_TEST_2		0xEA
+#define PM8058_CHG_TEST_3		0xEB
+#define PM8058_OVP_TEST_REG		0xF6
+#define FORCE_OVP_OFF			3
+
+#define PM8058_CHG_CNTRL		0x1E
+#define CHG_TRICKLE_EN			7
+#define CHG_USB_SUSPEND			6
+#define CHG_IMON_CAL			5
+#define CHG_IMON_GAIN			4
+#define CHG_VBUS_FROM_BOOST_OVRD	2
+#define CHG_CHARGE_DIS			1
+#define CHG_VCP_EN			0
+
+#define PM8058_CHG_CNTRL_2		0xD8
+#define ATC_DIS				7	/* coincell backed */
+#define CHARGE_AUTO_DIS			6
+#define DUMB_CHG_OVRD			5	/* coincell backed */
+#define ENUM_DONE			4
+#define CHG_TEMP_MODE			3
+#define CHG_BATT_TEMP_DIS		1	/* coincell backed */
+#define CHG_FAILED_CLEAR		0
+
+#define PM8058_CHG_VMAX_SEL		0x21
+#define PM8058_CHG_VBAT_DET		0xD9
+#define PM8058_CHG_IMAX			0x1F
+#define PM8058_CHG_TRICKLE		0xDB
+#define PM8058_CHG_ITERM		0xDC
+#define PM8058_CHG_TTRKL_MAX		0xE1
+#define PM8058_CHG_TCHG_MAX		0xE4
+#define PM8058_CHG_TEMP_THRESH		0xE2
+#define PM8058_CHG_TEMP_REG		0xE3
+#define PM8058_CHG_PULSE		0x22
+
+/* IRQ STATUS and CLEAR */
+#define PM8058_CHG_STATUS_CLEAR_IRQ_1	0x31
+#define PM8058_CHG_STATUS_CLEAR_IRQ_3	0x33
+#define PM8058_CHG_STATUS_CLEAR_IRQ_10	0xB3
+#define PM8058_CHG_STATUS_CLEAR_IRQ_11	0xB4
+
+/* IRQ MASKS */
+#define PM8058_CHG_MASK_IRQ_1		0x38
+
+#define PM8058_CHG_MASK_IRQ_3		0x3A
+#define PM8058_CHG_MASK_IRQ_10		0xBA
+#define PM8058_CHG_MASK_IRQ_11		0xBB
+
+/* IRQ Real time status regs */
+#define PM8058_CHG_STATUS_RT_1		0x3F
+#define STATUS_RTCHGVAL			7
+#define STATUS_RTCHGINVAL		6
+#define STATUS_RTBATT_REPLACE		5
+#define STATUS_RTVBATDET_LOW		4
+#define STATUS_RTCHGILIM		3
+#define STATUS_RTPCTDONE		1
+#define STATUS_RTVCP			0
+#define PM8058_CHG_STATUS_RT_3		0x41
+#define PM8058_CHG_STATUS_RT_10		0xC1
+#define PM8058_CHG_STATUS_RT_11		0xC2
+
+/* VTRIM */
+#define PM8058_CHG_VTRIM		0x1D
+#define PM8058_CHG_VBATDET_TRIM		0x1E
+#define PM8058_CHG_ITRIM		0x1F
+#define PM8058_CHG_TTRIM		0x20
+
+#define AUTO_CHARGING_VMAXSEL				4200
+#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES		512
+#define AUTO_CHARGING_TRICKLE_TIME_MINUTES		30
+#define AUTO_CHARGING_VEOC_ITERM			100
+#define AUTO_CHARGING_IEOC_ITERM			160
+
+#define AUTO_CHARGING_VBATDET				4150
+#define AUTO_CHARGING_VEOC_VBATDET			4100
+#define AUTO_CHARGING_VEOC_TCHG				16
+#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE		32
+#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS		5400000
+
+#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS	60000
+#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER	5
+
+#define PM8058_CHG_I_STEP_MA 50
+#define PM8058_CHG_I_MIN_MA 50
+#define PM8058_CHG_T_TCHG_SHIFT 2
+#define PM8058_CHG_I_TERM_STEP_MA 10
+#define PM8058_CHG_V_STEP_MV 25
+#define PM8058_CHG_V_MIN_MV  2400
+/*
+ * enum pmic_chg_interrupts: pmic interrupts
+ * @CHGVAL_IRQ: charger V between 3.3 and 7.9
+ * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
+ * @VBATDET_LOW_IRQ: VBAT < VBATDET
+ * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
+ * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
+ * @ATC_DONE_IRQ: Auto Trickle done
+ * @ATCFAIL_IRQ: Auto Trickle fail
+ * @AUTO_CHGDONE_IRQ: Auto chg done
+ * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
+ * @CHGSTATE_IRQ: something happend causing a state change
+ * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
+ * @CHG_END_IRQ: mA has dropped to termination current
+ * @BATTTEMP_IRQ: batt temp is out of range
+ * @CHGHOT_IRQ: the pass device is too hot
+ * @CHGTLIMIT_IRQ: unused
+ * @CHG_GONE_IRQ: charger was removed
+ * @VCPMAJOR_IRQ: vcp major
+ * @VBATDET_IRQ: VBAT >= VBATDET
+ * @BATFET_IRQ: BATFET closed
+ * @BATT_REPLACE_IRQ:
+ * @BATTCONNECT_IRQ:
+ */
+enum pmic_chg_interrupts {
+	CHGVAL_IRQ,
+	CHGINVAL_IRQ,
+	VBATDET_LOW_IRQ,
+	VCP_IRQ,
+	CHGILIM_IRQ,
+	ATC_DONE_IRQ,
+	ATCFAIL_IRQ,
+	AUTO_CHGDONE_IRQ,
+	AUTO_CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	FASTCHG_IRQ,
+	CHG_END_IRQ,
+	BATTTEMP_IRQ,
+	CHGHOT_IRQ,
+	CHGTLIMIT_IRQ,
+	CHG_GONE_IRQ,
+	VCPMAJOR_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	BATT_REPLACE_IRQ,
+	BATTCONNECT_IRQ,
+	PMIC_CHG_MAX_INTS
+};
+
+struct pm8058_charger {
+	struct pmic_charger_pdata *pdata;
+	struct pm8058_chip *pm_chip;
+	struct device *dev;
+
+	int pmic_chg_irq[PMIC_CHG_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	struct delayed_work check_vbat_low_work;
+	struct delayed_work veoc_begin_work;
+	int waiting_for_topoff;
+	int waiting_for_veoc;
+	int current_charger_current;
+
+	struct msm_xo_voter *voter;
+	struct dentry *dent;
+};
+
+static struct pm8058_charger pm8058_chg;
+
+static int pm_chg_get_rt_status(int irq)
+{
+	int count = 3;
+	int ret;
+
+	while ((ret =
+		pm8058_irq_get_rt_status(pm8058_chg.pm_chip, irq)) == -EAGAIN
+	       && count--) {
+		dev_info(pm8058_chg.dev, "%s trycount=%d\n", __func__, count);
+		cpu_relax();
+	}
+	if (ret == -EAGAIN)
+		return 0;
+	else
+		return ret;
+}
+
+static int is_chg_plugged_in(void)
+{
+	return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+}
+
+static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (!is_chg_plugged_in()) {	/*this debounces it */
+		ret = pm8058_read(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&old, 1);
+		temp = old | BIT(FORCE_OVP_OFF);
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&temp, 1);
+		temp = 0xFC;
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+					&temp, 1);
+		pr_debug("%s forced wrote 0xFC to test ret=%d\n",
+							__func__, ret);
+		/* 20 ms sleep is for the VCHG to discharge */
+		msleep(20);
+		temp = 0xF0;
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+					&temp, 1);
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&old, 1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void free_irqs(void)
+{
+	int i;
+
+	for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
+		if (pm8058_chg.pmic_chg_irq[i]) {
+			free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
+			pm8058_chg.pmic_chg_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	ret = 0;
+	bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_any_context_irq(res->start,
+				  pm8058_chg_chgval_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
+		}
+	}
+
+	return 0;
+
+err_out:
+	free_irqs();
+	return -EINVAL;
+}
+
+static int pm8058_usb_voltage_lower_limit(void)
+{
+	u8 temp, old;
+	int ret = 0;
+
+	temp = 0x10;
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+	ret |= pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST, &old, 1);
+	old = old & ~BIT(IGNORE_LL);
+	temp = 0x90  | (0xF & old);
+	pr_debug("%s writing 0x%x to test\n", __func__, temp);
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+
+	return ret;
+}
+
+static int __devinit pm8058_charger_probe(struct platform_device *pdev)
+{
+	struct pm8058_chip *pm_chip;
+
+	pm_chip = dev_get_drvdata(pdev->dev.parent);
+	if (pm_chip == NULL) {
+		pr_err("%s:no parent data passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	pm8058_chg.pm_chip = pm_chip;
+	pm8058_chg.pdata = pdev->dev.platform_data;
+	pm8058_chg.dev = &pdev->dev;
+
+	if (request_irqs(pdev)) {
+		pr_err("%s: couldnt register interrupts\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pm8058_usb_voltage_lower_limit()) {
+		pr_err("%s: couldnt write to IGNORE_LL\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __devexit pm8058_charger_remove(struct platform_device *pdev)
+{
+	free_irqs();
+	return 0;
+}
+
+static struct platform_driver pm8058_charger_driver = {
+	.probe = pm8058_charger_probe,
+	.remove = __devexit_p(pm8058_charger_remove),
+	.driver = {
+		   .name = "pm-usb-fix",
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_charger_init(void)
+{
+	return platform_driver_register(&pm8058_charger_driver);
+}
+
+static void __exit pm8058_charger_exit(void)
+{
+	platform_driver_unregister(&pm8058_charger_driver);
+}
+
+late_initcall(pm8058_charger_init);
+module_exit(pm8058_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058_charger");