diff options
-rw-r--r-- | drivers/power/supply/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/supply/pm2301_charger.c | 1249 | ||||
-rw-r--r-- | include/linux/pm2301_charger.h | 48 |
3 files changed, 1 insertions, 1298 deletions
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index a7309a3d1a47..16ebfaf6d55b 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -60,7 +60,7 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o -obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o diff --git a/drivers/power/supply/pm2301_charger.c b/drivers/power/supply/pm2301_charger.c deleted file mode 100644 index f86bbbeaff6c..000000000000 --- a/drivers/power/supply/pm2301_charger.c +++ /dev/null @@ -1,1249 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2012 ST Ericsson. - * - * Power supply driver for ST Ericsson pm2xxx_charger charger - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/power_supply.h> -#include <linux/regulator/consumer.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/workqueue.h> -#include <linux/mfd/abx500/ab8500.h> -#include <linux/pm2301_charger.h> -#include <linux/gpio.h> -#include <linux/pm_runtime.h> -#include <linux/pm.h> - -#include "ab8500-bm.h" -#include "ab8500-chargalg.h" -#include "pm2301_charger.h" - -#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \ - struct pm2xxx_charger, ac_chg) -#define SLEEP_MIN 50 -#define SLEEP_MAX 100 -#define PM2XXX_AUTOSUSPEND_DELAY 500 - -static int pm2xxx_interrupt_registers[] = { - PM2XXX_REG_INT1, - PM2XXX_REG_INT2, - PM2XXX_REG_INT3, - PM2XXX_REG_INT4, - PM2XXX_REG_INT5, - PM2XXX_REG_INT6, -}; - -static enum power_supply_property pm2xxx_charger_ac_props[] = { - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_AVG, -}; - -static int pm2xxx_charger_voltage_map[] = { - 3500, - 3525, - 3550, - 3575, - 3600, - 3625, - 3650, - 3675, - 3700, - 3725, - 3750, - 3775, - 3800, - 3825, - 3850, - 3875, - 3900, - 3925, - 3950, - 3975, - 4000, - 4025, - 4050, - 4075, - 4100, - 4125, - 4150, - 4175, - 4200, - 4225, - 4250, - 4275, - 4300, -}; - -static int pm2xxx_charger_current_map[] = { - 200, - 200, - 400, - 600, - 800, - 1000, - 1200, - 1400, - 1600, - 1800, - 2000, - 2200, - 2400, - 2600, - 2800, - 3000, -}; - -static void set_lpn_pin(struct pm2xxx_charger *pm2) -{ - if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) { - gpio_set_value(pm2->lpn_pin, 1); - usleep_range(SLEEP_MIN, SLEEP_MAX); - } -} - -static void clear_lpn_pin(struct pm2xxx_charger *pm2) -{ - if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) - gpio_set_value(pm2->lpn_pin, 0); -} - -static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) -{ - int ret; - - /* wake up the device */ - pm_runtime_get_sync(pm2->dev); - - ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, - 1, val); - if (ret < 0) - dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); - else - ret = 0; - - pm_runtime_put_sync(pm2->dev); - - return ret; -} - -static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) -{ - int ret; - - /* wake up the device */ - pm_runtime_get_sync(pm2->dev); - - ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, - 1, &val); - if (ret < 0) - dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); - else - ret = 0; - - pm_runtime_put_sync(pm2->dev); - - return ret; -} - -static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2) -{ - int ret; - - /* Enable charging */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, - (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA)); - - return ret; -} - -static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2) -{ - int ret; - - /* Disable SW EOC ctrl */ - ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - - /* Disable charging */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, - (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS)); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - - return 0; -} - -static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val) -{ - queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); - - return 0; -} - - -static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) -{ - queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); - - return 0; -} - -static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val) -{ - dev_err(pm2->dev, "Overvoltage detected\n"); - pm2->flags.ovv = true; - power_supply_changed(pm2->ac_chg.psy); - - /* Schedule a new HW failure check */ - queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0); - - return 0; -} - -static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val) -{ - dev_dbg(pm2->dev , "20 minutes watchdog expired\n"); - - pm2->ac.wd_expired = true; - power_supply_changed(pm2->ac_chg.psy); - - return 0; -} - -static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val) -{ - int ret; - - switch (val) { - case PM2XXX_INT1_ITVBATLOWR: - dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n"); - /* Enable SW EOC ctrl */ - ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, - PM2XXX_SWCTRL_SW); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - break; - - case PM2XXX_INT1_ITVBATLOWF: - dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n"); - /* Disable SW EOC ctrl */ - ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, - PM2XXX_SWCTRL_HW); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - break; - - default: - dev_err(pm2->dev, "Unknown VBAT level\n"); - } - - return 0; -} - -static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val) -{ - dev_dbg(pm2->dev, "battery disconnected\n"); - - return 0; -} - -static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val) -{ - int ret; - - ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val); - - if (ret < 0) { - dev_err(pm2->dev, "Charger detection failed\n"); - goto out; - } - - *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG); - -out: - return ret; -} - -static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val) -{ - - int ret; - u8 read_val; - - /* - * Since we can't be sure that the events are received - * synchronously, we have the check if the main charger is - * connected by reading the interrupt source register. - */ - ret = pm2xxx_charger_detection(pm2, &read_val); - - if ((ret == 0) && read_val) { - pm2->ac.charger_connected = 1; - pm2->ac_conn = true; - queue_work(pm2->charger_wq, &pm2->ac_work); - } - - - return ret; -} - -static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2, - int val) -{ - pm2->ac.charger_connected = 0; - queue_work(pm2->charger_wq, &pm2->ac_work); - - return 0; -} - -static int pm2_int_reg0(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & PM2XXX_INT1_ITVBATLOWR) { - ret = pm2xxx_charger_vbat_lsig_mngt(pm2, - PM2XXX_INT1_ITVBATLOWR); - if (ret < 0) - goto out; - } - - if (val & PM2XXX_INT1_ITVBATLOWF) { - ret = pm2xxx_charger_vbat_lsig_mngt(pm2, - PM2XXX_INT1_ITVBATLOWF); - if (ret < 0) - goto out; - } - - if (val & PM2XXX_INT1_ITVBATDISCONNECT) { - ret = pm2xxx_charger_bat_disc_mngt(pm2, - PM2XXX_INT1_ITVBATDISCONNECT); - if (ret < 0) - goto out; - } -out: - return ret; -} - -static int pm2_int_reg1(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) { - dev_dbg(pm2->dev , "Main charger plugged\n"); - ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val & - (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)); - } - - if (val & - (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) { - dev_dbg(pm2->dev , "Main charger unplugged\n"); - ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val & - (PM2XXX_INT2_ITVPWR1UNPLUG | - PM2XXX_INT2_ITVPWR2UNPLUG)); - } - - return ret; -} - -static int pm2_int_reg2(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD) - ret = pm2xxx_charger_wd_exp_mngt(pm2, val); - - if (val & (PM2XXX_INT3_ITCHPRECHARGEWD | - PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) { - dev_dbg(pm2->dev, - "Watchdog occurred for precharge, CC and CV charge\n"); - } - - return ret; -} - -static int pm2_int_reg3(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & (PM2XXX_INT4_ITCHARGINGON)) { - dev_dbg(pm2->dev , - "charging operation has started\n"); - } - - if (val & (PM2XXX_INT4_ITVRESUME)) { - dev_dbg(pm2->dev, - "battery discharged down to VResume threshold\n"); - } - - if (val & (PM2XXX_INT4_ITBATTFULL)) { - dev_dbg(pm2->dev , "battery fully detected\n"); - } - - if (val & (PM2XXX_INT4_ITCVPHASE)) { - dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n"); - } - - if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) { - pm2->failure_case = VPWR_OVV; - ret = pm2xxx_charger_ovv_mngt(pm2, val & - (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)); - dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n"); - } - - if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD | - PM2XXX_INT4_S_ITBATTEMPHOT)) { - ret = pm2xxx_charger_batt_therm_mngt(pm2, val & - (PM2XXX_INT4_S_ITBATTEMPCOLD | - PM2XXX_INT4_S_ITBATTEMPHOT)); - dev_dbg(pm2->dev, "BTEMP is too Low/High\n"); - } - - return ret; -} - -static int pm2_int_reg4(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & PM2XXX_INT5_ITVSYSTEMOVV) { - pm2->failure_case = VSYSTEM_OVV; - ret = pm2xxx_charger_ovv_mngt(pm2, val & - PM2XXX_INT5_ITVSYSTEMOVV); - dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n"); - } - - if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL | - PM2XXX_INT5_ITTHERMALWARNINGRISE | - PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | - PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) { - dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n"); - ret = pm2xxx_charger_die_therm_mngt(pm2, val & - (PM2XXX_INT5_ITTHERMALWARNINGFALL | - PM2XXX_INT5_ITTHERMALWARNINGRISE | - PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | - PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)); - } - - return ret; -} - -static int pm2_int_reg5(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - - if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) { - dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n"); - } - - if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE | - PM2XXX_INT6_ITVPWR1VALIDRISE | - PM2XXX_INT6_ITVPWR2VALIDFALL | - PM2XXX_INT6_ITVPWR1VALIDFALL)) { - dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n"); - } - - return 0; -} - -static irqreturn_t pm2xxx_irq_int(int irq, void *data) -{ - struct pm2xxx_charger *pm2 = data; - struct pm2xxx_interrupts *interrupt = pm2->pm2_int; - int i; - - /* wake up the device */ - pm_runtime_get_sync(pm2->dev); - - do { - for (i = 0; i < PM2XXX_NUM_INT_REG; i++) { - pm2xxx_reg_read(pm2, - pm2xxx_interrupt_registers[i], - &(interrupt->reg[i])); - - if (interrupt->reg[i] > 0) - interrupt->handler[i](pm2, interrupt->reg[i]); - } - } while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0); - - pm_runtime_mark_last_busy(pm2->dev); - pm_runtime_put_autosuspend(pm2->dev); - - return IRQ_HANDLED; -} - -static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2) -{ - int ret = 0; - u8 val; - - if (pm2->ac.charger_connected && pm2->ac.charger_online) { - - ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - goto out; - } - - if (val & PM2XXX_INT4_S_ITCVPHASE) - ret = PM2XXX_CONST_VOLT; - else - ret = PM2XXX_CONST_CURR; - } -out: - return ret; -} - -static int pm2xxx_current_to_regval(int curr) -{ - int i; - - if (curr < pm2xxx_charger_current_map[0]) - return 0; - - for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) { - if (curr < pm2xxx_charger_current_map[i]) - return (i - 1); - } - - i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1; - if (curr == pm2xxx_charger_current_map[i]) - return i; - else - return -EINVAL; -} - -static int pm2xxx_voltage_to_regval(int curr) -{ - int i; - - if (curr < pm2xxx_charger_voltage_map[0]) - return 0; - - for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) { - if (curr < pm2xxx_charger_voltage_map[i]) - return i - 1; - } - - i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1; - if (curr == pm2xxx_charger_voltage_map[i]) - return i; - else - return -EINVAL; -} - -static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger, - int ich_out) -{ - int ret; - int curr_index; - struct pm2xxx_charger *pm2; - u8 val; - - if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) - pm2 = to_pm2xxx_charger_ac_device_info(charger); - else - return -ENXIO; - - curr_index = pm2xxx_current_to_regval(ich_out); - if (curr_index < 0) { - dev_err(pm2->dev, - "Charger current too high, charging not started\n"); - return -ENXIO; - } - - ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); - if (ret >= 0) { - val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; - val |= curr_index; - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); - if (ret < 0) { - dev_err(pm2->dev, - "%s write failed\n", __func__); - } - } - else - dev_err(pm2->dev, "%s read failed\n", __func__); - - return ret; -} - -static int pm2xxx_charger_ac_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct pm2xxx_charger *pm2; - - pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy)); - - switch (psp) { - case POWER_SUPPLY_PROP_HEALTH: - if (pm2->flags.mainextchnotok) - val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - else if (pm2->ac.wd_expired) - val->intval = POWER_SUPPLY_HEALTH_DEAD; - else if (pm2->flags.main_thermal_prot) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (pm2->flags.ovv) - val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - else - val->intval = POWER_SUPPLY_HEALTH_GOOD; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = pm2->ac.charger_online; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = pm2->ac.charger_connected; - break; - case POWER_SUPPLY_PROP_VOLTAGE_AVG: - pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2); - val->intval = pm2->ac.cv_active; - break; - default: - return -EINVAL; - } - return 0; -} - -static int pm2xxx_charging_init(struct pm2xxx_charger *pm2) -{ - int ret = 0; - - /* enable CC and CV watchdog */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3, - (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN)); - if( ret < 0) - return ret; - - /* enable precharge watchdog */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4, - PM2XXX_CH_WD_PRECH_PHASE_60MIN); - - /* Disable auto timeout */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5, - PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN); - - /* - * EOC current level = 100mA - * Precharge current level = 100mA - * CC current level = 1000mA - */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, - (PM2XXX_DIR_CH_CC_CURRENT_1000MA | - PM2XXX_CH_PRECH_CURRENT_100MA | - PM2XXX_CH_EOC_CURRENT_100MA)); - - /* - * recharge threshold = 3.8V - * Precharge to CC threshold = 2.9V - */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7, - (PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8)); - - /* float voltage charger level = 4.2V */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, - PM2XXX_CH_VOLT_4_2); - - /* Voltage drop between VBAT and VSYS in HW charging = 300mV */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9, - (PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS | - PM2XXX_CH_CC_REDUCED_CURRENT_IDENT | - PM2XXX_CH_CC_MODEDROP_DIS)); - - /* Input charger level of over voltage = 10V */ - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2, - PM2XXX_VPWR2_OVV_10); - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1, - PM2XXX_VPWR1_OVV_10); - - /* Input charger drop */ - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2, - (PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS | - PM2XXX_VPWR2_DROP_DIS)); - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1, - (PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS | - PM2XXX_VPWR1_DROP_DIS)); - - /* Disable battery low monitoring */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG, - PM2XXX_VBAT_LOW_MONITORING_ENA); - - return ret; -} - -static int pm2xxx_charger_ac_en(struct ux500_charger *charger, - int enable, int vset, int iset) -{ - int ret; - int volt_index; - int curr_index; - u8 val; - - struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger); - - if (enable) { - if (!pm2->ac.charger_connected) { - dev_dbg(pm2->dev, "AC charger not connected\n"); - return -ENXIO; - } - - dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset); - if (!pm2->vddadc_en_ac) { - ret = regulator_enable(pm2->regu); - if (ret) - dev_warn(pm2->dev, - "Failed to enable vddadc regulator\n"); - else - pm2->vddadc_en_ac = true; - } - - ret = pm2xxx_charging_init(pm2); - if (ret < 0) { - dev_err(pm2->dev, "%s charging init failed\n", - __func__); - goto error_occured; - } - - volt_index = pm2xxx_voltage_to_regval(vset); - curr_index = pm2xxx_current_to_regval(iset); - - if (volt_index < 0 || curr_index < 0) { - dev_err(pm2->dev, - "Charger voltage or current too high, " - "charging not started\n"); - return -ENXIO; - } - - ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - goto error_occured; - } - val &= ~PM2XXX_CH_VOLT_MASK; - val |= volt_index; - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - goto error_occured; - } - - ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - goto error_occured; - } - val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; - val |= curr_index; - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - goto error_occured; - } - - if (!pm2->bat->enable_overshoot) { - ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", - __func__); - goto error_occured; - } - val |= PM2XXX_ANTI_OVERSHOOT_EN; - ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", - __func__); - goto error_occured; - } - } - - ret = pm2xxx_charging_enable_mngt(pm2); - if (ret < 0) { - dev_err(pm2->dev, "Failed to enable" - "pm2xxx ac charger\n"); - goto error_occured; - } - - pm2->ac.charger_online = 1; - } else { - pm2->ac.charger_online = 0; - pm2->ac.wd_expired = false; - - /* Disable regulator if enabled */ - if (pm2->vddadc_en_ac) { - regulator_disable(pm2->regu); - pm2->vddadc_en_ac = false; - } - - ret = pm2xxx_charging_disable_mngt(pm2); - if (ret < 0) { - dev_err(pm2->dev, "failed to disable" - "pm2xxx ac charger\n"); - goto error_occured; - } - - dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n"); - } - power_supply_changed(pm2->ac_chg.psy); - -error_occured: - return ret; -} - -static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger) -{ - int ret; - struct pm2xxx_charger *pm2; - - if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) - pm2 = to_pm2xxx_charger_ac_device_info(charger); - else - return -ENXIO; - - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER); - if (ret) - dev_err(pm2->dev, "Failed to kick WD!\n"); - - return ret; -} - -static void pm2xxx_charger_ac_work(struct work_struct *work) -{ - struct pm2xxx_charger *pm2 = container_of(work, - struct pm2xxx_charger, ac_work); - - - power_supply_changed(pm2->ac_chg.psy); - sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); -}; - -static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work) -{ - u8 reg_value; - - struct pm2xxx_charger *pm2 = container_of(work, - struct pm2xxx_charger, check_hw_failure_work.work); - - if (pm2->flags.ovv) { - pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, ®_value); - - if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV | - PM2XXX_INT4_S_ITVPWR2OVV))) { - pm2->flags.ovv = false; - power_supply_changed(pm2->ac_chg.psy); - } - } - - /* If we still have a failure, schedule a new check */ - if (pm2->flags.ovv) { - queue_delayed_work(pm2->charger_wq, - &pm2->check_hw_failure_work, round_jiffies(HZ)); - } -} - -static void pm2xxx_charger_check_main_thermal_prot_work( - struct work_struct *work) -{ - int ret; - u8 val; - - struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger, - check_main_thermal_prot_work); - - /* Check if die temp warning is still active */ - ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - return; - } - if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE - | PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE)) - pm2->flags.main_thermal_prot = true; - else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL - | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL)) - pm2->flags.main_thermal_prot = false; - - power_supply_changed(pm2->ac_chg.psy); -} - -static struct pm2xxx_interrupts pm2xxx_int = { - .handler[0] = pm2_int_reg0, - .handler[1] = pm2_int_reg1, - .handler[2] = pm2_int_reg2, - .handler[3] = pm2_int_reg3, - .handler[4] = pm2_int_reg4, - .handler[5] = pm2_int_reg5, -}; - -static struct pm2xxx_irq pm2xxx_charger_irq[] = { - {"PM2XXX_IRQ_INT", pm2xxx_irq_int}, -}; - -static int __maybe_unused pm2xxx_wall_charger_resume(struct device *dev) -{ - struct i2c_client *i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client); - set_lpn_pin(pm2); - - /* If we still have a HW failure, schedule a new check */ - if (pm2->flags.ovv) - queue_delayed_work(pm2->charger_wq, - &pm2->check_hw_failure_work, 0); - - return 0; -} - -static int __maybe_unused pm2xxx_wall_charger_suspend(struct device *dev) -{ - struct i2c_client *i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client); - clear_lpn_pin(pm2); - - /* Cancel any pending HW failure check */ - if (delayed_work_pending(&pm2->check_hw_failure_work)) - cancel_delayed_work(&pm2->check_hw_failure_work); - - flush_work(&pm2->ac_work); - flush_work(&pm2->check_main_thermal_prot_work); - - return 0; -} - -static int __maybe_unused pm2xxx_runtime_suspend(struct device *dev) -{ - struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); - clear_lpn_pin(pm2); - - return 0; -} - -static int __maybe_unused pm2xxx_runtime_resume(struct device *dev) -{ - struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); - - if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0) - set_lpn_pin(pm2); - - return 0; -} - -static const struct dev_pm_ops pm2xxx_pm_ops __maybe_unused = { - SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend, - pm2xxx_wall_charger_resume) - SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL) -}; - -static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) -{ - struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data; - struct power_supply_config psy_cfg = {}; - struct pm2xxx_charger *pm2; - int ret = 0; - u8 val; - int i; - - if (!pl_data) { - dev_err(&i2c_client->dev, "No platform data supplied\n"); - return -EINVAL; - } - - pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); - if (!pm2) { - dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed\n"); - return -ENOMEM; - } - - /* get parent data */ - pm2->dev = &i2c_client->dev; - - pm2->pm2_int = &pm2xxx_int; - - /* get charger spcific platform data */ - if (!pl_data->wall_charger) { - dev_err(pm2->dev, "no charger platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - - pm2->pdata = pl_data->wall_charger; - - /* get battery specific platform data */ - if (!pl_data->battery) { - dev_err(pm2->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - - pm2->bat = pl_data->battery; - - if (!i2c_check_functionality(i2c_client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_READ_WORD_DATA)) { - ret = -ENODEV; - dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n"); - goto free_device_info; - } - - pm2->config.pm2xxx_i2c = i2c_client; - pm2->config.pm2xxx_id = (struct i2c_device_id *) id; - i2c_set_clientdata(i2c_client, pm2); - - /* AC supply */ - /* power_supply base class */ - pm2->ac_chg_desc.name = pm2->pdata->label; - pm2->ac_chg_desc.type = POWER_SUPPLY_TYPE_MAINS; - pm2->ac_chg_desc.properties = pm2xxx_charger_ac_props; - pm2->ac_chg_desc.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); - pm2->ac_chg_desc.get_property = pm2xxx_charger_ac_get_property; - - psy_cfg.supplied_to = pm2->pdata->supplied_to; - psy_cfg.num_supplicants = pm2->pdata->num_supplicants; - /* pm2xxx_charger sub-class */ - pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en; - pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick; - pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current; - pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[ - ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1]; - pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[ - ARRAY_SIZE(pm2xxx_charger_current_map) - 1]; - pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL; - pm2->ac_chg.enabled = true; - pm2->ac_chg.external = true; - - /* Create a work queue for the charger */ - pm2->charger_wq = alloc_ordered_workqueue("pm2xxx_charger_wq", - WQ_MEM_RECLAIM); - if (pm2->charger_wq == NULL) { - ret = -ENOMEM; - dev_err(pm2->dev, "failed to create work queue\n"); - goto free_device_info; - } - - /* Init work for charger detection */ - INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work); - - /* Init work for checking HW status */ - INIT_WORK(&pm2->check_main_thermal_prot_work, - pm2xxx_charger_check_main_thermal_prot_work); - - /* Init work for HW failure check */ - INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work, - pm2xxx_charger_check_hw_failure_work); - - /* - * VDD ADC supply needs to be enabled from this driver when there - * is a charger connected to avoid erroneous BTEMP_HIGH/LOW - * interrupts during charging - */ - pm2->regu = regulator_get(pm2->dev, "vddadc"); - if (IS_ERR(pm2->regu)) { - ret = PTR_ERR(pm2->regu); - dev_err(pm2->dev, "failed to get vddadc regulator\n"); - goto free_charger_wq; - } - - /* Register AC charger class */ - pm2->ac_chg.psy = power_supply_register(pm2->dev, &pm2->ac_chg_desc, - &psy_cfg); - if (IS_ERR(pm2->ac_chg.psy)) { - dev_err(pm2->dev, "failed to register AC charger\n"); - ret = PTR_ERR(pm2->ac_chg.psy); - goto free_regulator; - } - - /* Register interrupts */ - ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), - NULL, - pm2xxx_charger_irq[0].isr, - pm2->pdata->irq_type | IRQF_ONESHOT, - pm2xxx_charger_irq[0].name, pm2); - - if (ret != 0) { - dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n", - pm2xxx_charger_irq[0].name, - gpio_to_irq(pm2->pdata->gpio_irq_number), ret); - goto unregister_pm2xxx_charger; - } - - ret = pm_runtime_set_active(pm2->dev); - if (ret) - dev_err(pm2->dev, "set active Error\n"); - - pm_runtime_enable(pm2->dev); - pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(pm2->dev); - pm_runtime_resume(pm2->dev); - - /* pm interrupt can wake up system */ - ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); - if (ret) { - dev_err(pm2->dev, "failed to set irq wake\n"); - goto unregister_pm2xxx_interrupt; - } - - mutex_init(&pm2->lock); - - if (gpio_is_valid(pm2->pdata->lpn_gpio)) { - /* get lpn GPIO from platform data */ - pm2->lpn_pin = pm2->pdata->lpn_gpio; - - /* - * Charger detection mechanism requires pulling up the LPN pin - * while i2c communication if Charger is not connected - * LPN pin of PM2301 is GPIO60 of AB9540 - */ - ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio"); - - if (ret < 0) { - dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n"); - goto disable_pm2_irq_wake; - } - ret = gpio_direction_output(pm2->lpn_pin, 0); - if (ret < 0) { - dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n"); - goto free_gpio; - } - set_lpn_pin(pm2); - } - - /* read interrupt registers */ - for (i = 0; i < PM2XXX_NUM_INT_REG; i++) - pm2xxx_reg_read(pm2, - pm2xxx_interrupt_registers[i], - &val); - - ret = pm2xxx_charger_detection(pm2, &val); - - if ((ret == 0) && val) { - pm2->ac.charger_connected = 1; - ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON, - AB8500_MAIN_CH_DET); - pm2->ac_conn = true; - power_supply_changed(pm2->ac_chg.psy); - sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); - } - - return 0; - -free_gpio: - if (gpio_is_valid(pm2->lpn_pin)) - gpio_free(pm2->lpn_pin); -disable_pm2_irq_wake: - disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); -unregister_pm2xxx_interrupt: - /* disable interrupt */ - free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); -unregister_pm2xxx_charger: - /* unregister power supply */ - power_supply_unregister(pm2->ac_chg.psy); -free_regulator: - /* disable the regulator */ - regulator_put(pm2->regu); -free_charger_wq: - destroy_workqueue(pm2->charger_wq); -free_device_info: - kfree(pm2); - - return ret; -} - -static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) -{ - struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client); - - /* Disable pm_runtime */ - pm_runtime_disable(pm2->dev); - /* Disable AC charging */ - pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0); - - /* Disable wake by pm interrupt */ - disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); - - /* Disable interrupts */ - free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); - - /* Delete the work queue */ - destroy_workqueue(pm2->charger_wq); - - flush_scheduled_work(); - - /* disable the regulator */ - regulator_put(pm2->regu); - - power_supply_unregister(pm2->ac_chg.psy); - - if (gpio_is_valid(pm2->lpn_pin)) - gpio_free(pm2->lpn_pin); - - kfree(pm2); - - return 0; -} - -static const struct i2c_device_id pm2xxx_id[] = { - { "pm2301", 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, pm2xxx_id); - -static struct i2c_driver pm2xxx_charger_driver = { - .probe = pm2xxx_wall_charger_probe, - .remove = pm2xxx_wall_charger_remove, - .driver = { - .name = "pm2xxx-wall_charger", - .pm = IS_ENABLED(CONFIG_PM) ? &pm2xxx_pm_ops : NULL, - }, - .id_table = pm2xxx_id, -}; - -static int __init pm2xxx_charger_init(void) -{ - return i2c_add_driver(&pm2xxx_charger_driver); -} - -static void __exit pm2xxx_charger_exit(void) -{ - i2c_del_driver(&pm2xxx_charger_driver); -} - -device_initcall_sync(pm2xxx_charger_init); -module_exit(pm2xxx_charger_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay"); -MODULE_DESCRIPTION("PM2xxx charger management driver"); diff --git a/include/linux/pm2301_charger.h b/include/linux/pm2301_charger.h deleted file mode 100644 index b8fac96f05aa..000000000000 --- a/include/linux/pm2301_charger.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * PM2301 charger driver. - * - * Copyright (C) 2012 ST Ericsson Corporation - * - * Contact: Olivier LAUNAY (olivier.launay@stericsson.com - */ - -#ifndef __LINUX_PM2301_H -#define __LINUX_PM2301_H - -/** - * struct pm2xxx_bm_charger_parameters - Charger specific parameters - * @ac_volt_max: maximum allowed AC charger voltage in mV - * @ac_curr_max: maximum allowed AC charger current in mA - */ -struct pm2xxx_bm_charger_parameters { - int ac_volt_max; - int ac_curr_max; -}; - -/** - * struct pm2xxx_bm_data - pm2xxx battery management data - * @enable_overshoot flag to enable VBAT overshoot control - * @chg_params charger parameters - */ -struct pm2xxx_bm_data { - bool enable_overshoot; - const struct pm2xxx_bm_charger_parameters *chg_params; -}; - -struct pm2xxx_charger_platform_data { - char **supplied_to; - size_t num_supplicants; - int i2c_bus; - const char *label; - int gpio_irq_number; - unsigned int lpn_gpio; - int irq_type; -}; - -struct pm2xxx_platform_data { - struct pm2xxx_charger_platform_data *wall_charger; - struct pm2xxx_bm_data *battery; -}; - -#endif /* __LINUX_PM2301_H */ |