123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- /* Copyright (c) 2010, The Linux Foundation. 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");
|