162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Motorola CPCAP PMIC battery charger driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Tony Lindgren <tony@atomide.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Rewritten for Linux power framework with some parts based on 862306a36Sopenharmony_ci * earlier driver found in the Motorola Linux kernel: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2009-2010 Motorola, Inc. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/atomic.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/notifier.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/power_supply.h> 2462306a36Sopenharmony_ci#include <linux/regmap.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2762306a36Sopenharmony_ci#include <linux/usb/phy_companion.h> 2862306a36Sopenharmony_ci#include <linux/phy/omap_usb.h> 2962306a36Sopenharmony_ci#include <linux/usb/otg.h> 3062306a36Sopenharmony_ci#include <linux/iio/consumer.h> 3162306a36Sopenharmony_ci#include <linux/mfd/motorola-cpcap.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * CPCAP_REG_CRM register bits. For documentation of somewhat similar hardware, 3562306a36Sopenharmony_ci * see NXP "MC13783 Power Management and Audio Circuit Users's Guide" 3662306a36Sopenharmony_ci * MC13783UG.pdf chapter "8.5 Battery Interface Register Summary". The registers 3762306a36Sopenharmony_ci * and values for CPCAP are different, but some of the internal components seem 3862306a36Sopenharmony_ci * similar. Also see the Motorola Linux kernel cpcap-regbits.h. CPCAP_REG_CHRGR_1 3962306a36Sopenharmony_ci * bits that seem to describe the CRM register. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define CPCAP_REG_CRM_UNUSED_641_15 BIT(15) /* 641 = register number */ 4262306a36Sopenharmony_ci#define CPCAP_REG_CRM_UNUSED_641_14 BIT(14) /* 641 = register number */ 4362306a36Sopenharmony_ci#define CPCAP_REG_CRM_CHRG_LED_EN BIT(13) /* Charger LED */ 4462306a36Sopenharmony_ci#define CPCAP_REG_CRM_RVRSMODE BIT(12) /* USB VBUS output enable */ 4562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_TR1 BIT(11) /* Trickle charge current */ 4662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_TR0 BIT(10) 4762306a36Sopenharmony_ci#define CPCAP_REG_CRM_FET_OVRD BIT(9) /* 0 = hardware, 1 = FET_CTRL */ 4862306a36Sopenharmony_ci#define CPCAP_REG_CRM_FET_CTRL BIT(8) /* BPFET 1 if FET_OVRD set */ 4962306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG3 BIT(7) /* Charge voltage bits */ 5062306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG2 BIT(6) 5162306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG1 BIT(5) 5262306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG0 BIT(4) 5362306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG3 BIT(3) /* Charge current bits */ 5462306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG2 BIT(2) 5562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG1 BIT(1) 5662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG0 BIT(0) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* CPCAP_REG_CRM trickle charge voltages */ 5962306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR(val) (((val) & 0x3) << 10) 6062306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A00 CPCAP_REG_CRM_TR(0x0) 6162306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A24 CPCAP_REG_CRM_TR(0x1) 6262306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A48 CPCAP_REG_CRM_TR(0x2) 6362306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A72 CPCAP_REG_CRM_TR(0x4) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * CPCAP_REG_CRM charge voltages based on the ADC channel 1 values. 6762306a36Sopenharmony_ci * Note that these register bits don't match MC13783UG.pdf VCHRG 6862306a36Sopenharmony_ci * register bits. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG(val) (((val) & 0xf) << 4) 7162306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_3V80 CPCAP_REG_CRM_VCHRG(0x0) 7262306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V10 CPCAP_REG_CRM_VCHRG(0x1) 7362306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V12 CPCAP_REG_CRM_VCHRG(0x2) 7462306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V15 CPCAP_REG_CRM_VCHRG(0x3) 7562306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V17 CPCAP_REG_CRM_VCHRG(0x4) 7662306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V20 CPCAP_REG_CRM_VCHRG(0x5) 7762306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V23 CPCAP_REG_CRM_VCHRG(0x6) 7862306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V25 CPCAP_REG_CRM_VCHRG(0x7) 7962306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V27 CPCAP_REG_CRM_VCHRG(0x8) 8062306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V30 CPCAP_REG_CRM_VCHRG(0x9) 8162306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V33 CPCAP_REG_CRM_VCHRG(0xa) 8262306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V35 CPCAP_REG_CRM_VCHRG(0xb) 8362306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V38 CPCAP_REG_CRM_VCHRG(0xc) 8462306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V40 CPCAP_REG_CRM_VCHRG(0xd) 8562306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V42 CPCAP_REG_CRM_VCHRG(0xe) 8662306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V44 CPCAP_REG_CRM_VCHRG(0xf) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * CPCAP_REG_CRM charge currents. These seem to match MC13783UG.pdf 9062306a36Sopenharmony_ci * values in "Table 8-3. Charge Path Regulator Current Limit 9162306a36Sopenharmony_ci * Characteristics" for the nominal values. 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Except 70mA and 1.596A and unlimited, these are simply 88.7mA / step. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG(val) (((val) & 0xf) << 0) 9662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A000 CPCAP_REG_CRM_ICHRG(0x0) 9762306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A070 CPCAP_REG_CRM_ICHRG(0x1) 9862306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A177 CPCAP_REG_CRM_ICHRG(0x2) 9962306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A266 CPCAP_REG_CRM_ICHRG(0x3) 10062306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A355 CPCAP_REG_CRM_ICHRG(0x4) 10162306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A443 CPCAP_REG_CRM_ICHRG(0x5) 10262306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A532 CPCAP_REG_CRM_ICHRG(0x6) 10362306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A621 CPCAP_REG_CRM_ICHRG(0x7) 10462306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A709 CPCAP_REG_CRM_ICHRG(0x8) 10562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A798 CPCAP_REG_CRM_ICHRG(0x9) 10662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A886 CPCAP_REG_CRM_ICHRG(0xa) 10762306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A975 CPCAP_REG_CRM_ICHRG(0xb) 10862306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_1A064 CPCAP_REG_CRM_ICHRG(0xc) 10962306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_1A152 CPCAP_REG_CRM_ICHRG(0xd) 11062306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_1A596 CPCAP_REG_CRM_ICHRG(0xe) 11162306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_NO_LIMIT CPCAP_REG_CRM_ICHRG(0xf) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* CPCAP_REG_VUSBC register bits needed for VBUS */ 11462306a36Sopenharmony_ci#define CPCAP_BIT_VBUS_SWITCH BIT(0) /* VBUS boost to 5V */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cienum { 11762306a36Sopenharmony_ci CPCAP_CHARGER_IIO_BATTDET, 11862306a36Sopenharmony_ci CPCAP_CHARGER_IIO_VOLTAGE, 11962306a36Sopenharmony_ci CPCAP_CHARGER_IIO_VBUS, 12062306a36Sopenharmony_ci CPCAP_CHARGER_IIO_CHRG_CURRENT, 12162306a36Sopenharmony_ci CPCAP_CHARGER_IIO_BATT_CURRENT, 12262306a36Sopenharmony_ci CPCAP_CHARGER_IIO_NR, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct cpcap_charger_ddata { 12662306a36Sopenharmony_ci struct device *dev; 12762306a36Sopenharmony_ci struct regmap *reg; 12862306a36Sopenharmony_ci struct list_head irq_list; 12962306a36Sopenharmony_ci struct delayed_work detect_work; 13062306a36Sopenharmony_ci struct delayed_work vbus_work; 13162306a36Sopenharmony_ci struct gpio_desc *gpio[2]; /* gpio_reven0 & 1 */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci struct iio_channel *channels[CPCAP_CHARGER_IIO_NR]; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci struct power_supply *usb; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci struct phy_companion comparator; /* For USB VBUS */ 13862306a36Sopenharmony_ci unsigned int vbus_enabled:1; 13962306a36Sopenharmony_ci unsigned int feeding_vbus:1; 14062306a36Sopenharmony_ci atomic_t active; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci int status; 14362306a36Sopenharmony_ci int voltage; 14462306a36Sopenharmony_ci int limit_current; 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistruct cpcap_interrupt_desc { 14862306a36Sopenharmony_ci int irq; 14962306a36Sopenharmony_ci struct list_head node; 15062306a36Sopenharmony_ci const char *name; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct cpcap_charger_ints_state { 15462306a36Sopenharmony_ci bool chrg_det; 15562306a36Sopenharmony_ci bool rvrs_chrg; 15662306a36Sopenharmony_ci bool vbusov; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci bool chrg_se1b; 15962306a36Sopenharmony_ci bool rvrs_mode; 16062306a36Sopenharmony_ci bool chrgcurr2; 16162306a36Sopenharmony_ci bool chrgcurr1; 16262306a36Sopenharmony_ci bool vbusvld; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci bool battdetb; 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic enum power_supply_property cpcap_charger_props[] = { 16862306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 16962306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 17062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 17162306a36Sopenharmony_ci POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 17262306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 17362306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct iio_channel *channel; 17962306a36Sopenharmony_ci int error, value = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci channel = ddata->channels[CPCAP_CHARGER_IIO_VOLTAGE]; 18262306a36Sopenharmony_ci error = iio_read_channel_processed(channel, &value); 18362306a36Sopenharmony_ci if (error < 0) { 18462306a36Sopenharmony_ci dev_warn(ddata->dev, "%s failed: %i\n", __func__, error); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return value; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int cpcap_charger_get_charge_current(struct cpcap_charger_ddata *ddata) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct iio_channel *channel; 19562306a36Sopenharmony_ci int error, value = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci channel = ddata->channels[CPCAP_CHARGER_IIO_CHRG_CURRENT]; 19862306a36Sopenharmony_ci error = iio_read_channel_processed(channel, &value); 19962306a36Sopenharmony_ci if (error < 0) { 20062306a36Sopenharmony_ci dev_warn(ddata->dev, "%s failed: %i\n", __func__, error); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return value; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int cpcap_charger_get_property(struct power_supply *psy, 20962306a36Sopenharmony_ci enum power_supply_property psp, 21062306a36Sopenharmony_ci union power_supply_propval *val) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (psp) { 21562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 21662306a36Sopenharmony_ci val->intval = ddata->status; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 21962306a36Sopenharmony_ci val->intval = ddata->limit_current; 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 22262306a36Sopenharmony_ci val->intval = ddata->voltage; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 22562306a36Sopenharmony_ci if (ddata->status == POWER_SUPPLY_STATUS_CHARGING) 22662306a36Sopenharmony_ci val->intval = cpcap_charger_get_charge_voltage(ddata) * 22762306a36Sopenharmony_ci 1000; 22862306a36Sopenharmony_ci else 22962306a36Sopenharmony_ci val->intval = 0; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 23262306a36Sopenharmony_ci if (ddata->status == POWER_SUPPLY_STATUS_CHARGING) 23362306a36Sopenharmony_ci val->intval = cpcap_charger_get_charge_current(ddata) * 23462306a36Sopenharmony_ci 1000; 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci val->intval = 0; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 23962306a36Sopenharmony_ci val->intval = ddata->status == POWER_SUPPLY_STATUS_CHARGING; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci default: 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int cpcap_charger_match_voltage(int voltage) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci switch (voltage) { 25162306a36Sopenharmony_ci case 0 ... 4100000 - 1: return 3800000; 25262306a36Sopenharmony_ci case 4100000 ... 4120000 - 1: return 4100000; 25362306a36Sopenharmony_ci case 4120000 ... 4150000 - 1: return 4120000; 25462306a36Sopenharmony_ci case 4150000 ... 4170000 - 1: return 4150000; 25562306a36Sopenharmony_ci case 4170000 ... 4200000 - 1: return 4170000; 25662306a36Sopenharmony_ci case 4200000 ... 4230000 - 1: return 4200000; 25762306a36Sopenharmony_ci case 4230000 ... 4250000 - 1: return 4230000; 25862306a36Sopenharmony_ci case 4250000 ... 4270000 - 1: return 4250000; 25962306a36Sopenharmony_ci case 4270000 ... 4300000 - 1: return 4270000; 26062306a36Sopenharmony_ci case 4300000 ... 4330000 - 1: return 4300000; 26162306a36Sopenharmony_ci case 4330000 ... 4350000 - 1: return 4330000; 26262306a36Sopenharmony_ci case 4350000 ... 4380000 - 1: return 4350000; 26362306a36Sopenharmony_ci case 4380000 ... 4400000 - 1: return 4380000; 26462306a36Sopenharmony_ci case 4400000 ... 4420000 - 1: return 4400000; 26562306a36Sopenharmony_ci case 4420000 ... 4440000 - 1: return 4420000; 26662306a36Sopenharmony_ci case 4440000: return 4440000; 26762306a36Sopenharmony_ci default: return 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int 27262306a36Sopenharmony_cicpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata *ddata) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci union power_supply_propval prop; 27562306a36Sopenharmony_ci struct power_supply *battery; 27662306a36Sopenharmony_ci int voltage = ddata->voltage; 27762306a36Sopenharmony_ci int error; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci battery = power_supply_get_by_name("battery"); 28062306a36Sopenharmony_ci if (battery) { 28162306a36Sopenharmony_ci error = power_supply_get_property(battery, 28262306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 28362306a36Sopenharmony_ci &prop); 28462306a36Sopenharmony_ci if (!error) 28562306a36Sopenharmony_ci voltage = prop.intval; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci power_supply_put(battery); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return voltage; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int cpcap_charger_current_to_regval(int microamp) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int miliamp = microamp / 1000; 29662306a36Sopenharmony_ci int res; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (miliamp < 0) 29962306a36Sopenharmony_ci return -EINVAL; 30062306a36Sopenharmony_ci if (miliamp < 70) 30162306a36Sopenharmony_ci return CPCAP_REG_CRM_ICHRG(0x0); 30262306a36Sopenharmony_ci if (miliamp < 177) 30362306a36Sopenharmony_ci return CPCAP_REG_CRM_ICHRG(0x1); 30462306a36Sopenharmony_ci if (miliamp >= 1596) 30562306a36Sopenharmony_ci return CPCAP_REG_CRM_ICHRG(0xe); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci res = microamp / 88666; 30862306a36Sopenharmony_ci if (res > 0xd) 30962306a36Sopenharmony_ci res = 0xd; 31062306a36Sopenharmony_ci return CPCAP_REG_CRM_ICHRG(res); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int cpcap_charger_set_property(struct power_supply *psy, 31462306a36Sopenharmony_ci enum power_supply_property psp, 31562306a36Sopenharmony_ci const union power_supply_propval *val) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent); 31862306a36Sopenharmony_ci int voltage, batvolt; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci switch (psp) { 32162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 32262306a36Sopenharmony_ci if (cpcap_charger_current_to_regval(val->intval) < 0) 32362306a36Sopenharmony_ci return -EINVAL; 32462306a36Sopenharmony_ci ddata->limit_current = val->intval; 32562306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, 0); 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 32862306a36Sopenharmony_ci voltage = cpcap_charger_match_voltage(val->intval); 32962306a36Sopenharmony_ci batvolt = cpcap_charger_get_bat_const_charge_voltage(ddata); 33062306a36Sopenharmony_ci if (voltage > batvolt) 33162306a36Sopenharmony_ci voltage = batvolt; 33262306a36Sopenharmony_ci ddata->voltage = voltage; 33362306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, 0); 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci default: 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int cpcap_charger_property_is_writeable(struct power_supply *psy, 34362306a36Sopenharmony_ci enum power_supply_property psp) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci switch (psp) { 34662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 34762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 34862306a36Sopenharmony_ci return 1; 34962306a36Sopenharmony_ci default: 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void cpcap_charger_set_cable_path(struct cpcap_charger_ddata *ddata, 35562306a36Sopenharmony_ci bool enabled) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci if (!ddata->gpio[0]) 35862306a36Sopenharmony_ci return; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci gpiod_set_value(ddata->gpio[0], enabled); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void cpcap_charger_set_inductive_path(struct cpcap_charger_ddata *ddata, 36462306a36Sopenharmony_ci bool enabled) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci if (!ddata->gpio[1]) 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci gpiod_set_value(ddata->gpio[1], enabled); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata, 37362306a36Sopenharmony_ci int state) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci const char *status; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (state > POWER_SUPPLY_STATUS_FULL) { 37862306a36Sopenharmony_ci dev_warn(ddata->dev, "unknown state: %i\n", state); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ddata->status = state; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci switch (state) { 38662306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_DISCHARGING: 38762306a36Sopenharmony_ci status = "DISCONNECTED"; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_NOT_CHARGING: 39062306a36Sopenharmony_ci status = "DETECTING"; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_CHARGING: 39362306a36Sopenharmony_ci status = "CHARGING"; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_FULL: 39662306a36Sopenharmony_ci status = "DONE"; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci default: 39962306a36Sopenharmony_ci return; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci dev_dbg(ddata->dev, "state: %s\n", status); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int cpcap_charger_disable(struct cpcap_charger_ddata *ddata) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci int error; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 0x3fff, 41062306a36Sopenharmony_ci CPCAP_REG_CRM_FET_OVRD | 41162306a36Sopenharmony_ci CPCAP_REG_CRM_FET_CTRL); 41262306a36Sopenharmony_ci if (error) 41362306a36Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return error; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int cpcap_charger_enable(struct cpcap_charger_ddata *ddata, 41962306a36Sopenharmony_ci int max_voltage, int charge_current, 42062306a36Sopenharmony_ci int trickle_current) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci int error; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!max_voltage || !charge_current) 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci dev_dbg(ddata->dev, "enable: %i %i %i\n", 42862306a36Sopenharmony_ci max_voltage, charge_current, trickle_current); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 0x3fff, 43162306a36Sopenharmony_ci CPCAP_REG_CRM_CHRG_LED_EN | 43262306a36Sopenharmony_ci trickle_current | 43362306a36Sopenharmony_ci CPCAP_REG_CRM_FET_OVRD | 43462306a36Sopenharmony_ci CPCAP_REG_CRM_FET_CTRL | 43562306a36Sopenharmony_ci max_voltage | 43662306a36Sopenharmony_ci charge_current); 43762306a36Sopenharmony_ci if (error) 43862306a36Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return error; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic bool cpcap_charger_vbus_valid(struct cpcap_charger_ddata *ddata) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int error, value = 0; 44662306a36Sopenharmony_ci struct iio_channel *channel = 44762306a36Sopenharmony_ci ddata->channels[CPCAP_CHARGER_IIO_VBUS]; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci error = iio_read_channel_processed(channel, &value); 45062306a36Sopenharmony_ci if (error >= 0) 45162306a36Sopenharmony_ci return value > 3900; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dev_err(ddata->dev, "error reading VBUS: %i\n", error); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return false; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci/* VBUS control functions for the USB PHY companion */ 45962306a36Sopenharmony_cistatic void cpcap_charger_vbus_work(struct work_struct *work) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata; 46262306a36Sopenharmony_ci bool vbus = false; 46362306a36Sopenharmony_ci int error; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ddata = container_of(work, struct cpcap_charger_ddata, 46662306a36Sopenharmony_ci vbus_work.work); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (ddata->vbus_enabled) { 46962306a36Sopenharmony_ci vbus = cpcap_charger_vbus_valid(ddata); 47062306a36Sopenharmony_ci if (vbus) { 47162306a36Sopenharmony_ci dev_dbg(ddata->dev, "VBUS already provided\n"); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci ddata->feeding_vbus = true; 47762306a36Sopenharmony_ci cpcap_charger_set_cable_path(ddata, false); 47862306a36Sopenharmony_ci cpcap_charger_set_inductive_path(ddata, false); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci error = cpcap_charger_disable(ddata); 48162306a36Sopenharmony_ci if (error) 48262306a36Sopenharmony_ci goto out_err; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci cpcap_charger_update_state(ddata, 48562306a36Sopenharmony_ci POWER_SUPPLY_STATUS_DISCHARGING); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC, 48862306a36Sopenharmony_ci CPCAP_BIT_VBUS_SWITCH, 48962306a36Sopenharmony_ci CPCAP_BIT_VBUS_SWITCH); 49062306a36Sopenharmony_ci if (error) 49162306a36Sopenharmony_ci goto out_err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 49462306a36Sopenharmony_ci CPCAP_REG_CRM_RVRSMODE, 49562306a36Sopenharmony_ci CPCAP_REG_CRM_RVRSMODE); 49662306a36Sopenharmony_ci if (error) 49762306a36Sopenharmony_ci goto out_err; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC, 50062306a36Sopenharmony_ci CPCAP_BIT_VBUS_SWITCH, 0); 50162306a36Sopenharmony_ci if (error) 50262306a36Sopenharmony_ci goto out_err; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 50562306a36Sopenharmony_ci CPCAP_REG_CRM_RVRSMODE, 0); 50662306a36Sopenharmony_ci if (error) 50762306a36Sopenharmony_ci goto out_err; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci cpcap_charger_set_cable_path(ddata, true); 51062306a36Sopenharmony_ci cpcap_charger_set_inductive_path(ddata, true); 51162306a36Sopenharmony_ci ddata->feeding_vbus = false; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciout_err: 51762306a36Sopenharmony_ci cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN); 51862306a36Sopenharmony_ci dev_err(ddata->dev, "%s could not %s vbus: %i\n", __func__, 51962306a36Sopenharmony_ci ddata->vbus_enabled ? "enable" : "disable", error); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int cpcap_charger_set_vbus(struct phy_companion *comparator, 52362306a36Sopenharmony_ci bool enabled) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata = 52662306a36Sopenharmony_ci container_of(comparator, struct cpcap_charger_ddata, 52762306a36Sopenharmony_ci comparator); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ddata->vbus_enabled = enabled; 53062306a36Sopenharmony_ci schedule_delayed_work(&ddata->vbus_work, 0); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* Charger interrupt handling functions */ 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata, 53862306a36Sopenharmony_ci struct cpcap_charger_ints_state *s) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci int val, error; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS1, &val); 54362306a36Sopenharmony_ci if (error) 54462306a36Sopenharmony_ci return error; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci s->chrg_det = val & BIT(13); 54762306a36Sopenharmony_ci s->rvrs_chrg = val & BIT(12); 54862306a36Sopenharmony_ci s->vbusov = val & BIT(11); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS2, &val); 55162306a36Sopenharmony_ci if (error) 55262306a36Sopenharmony_ci return error; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci s->chrg_se1b = val & BIT(13); 55562306a36Sopenharmony_ci s->rvrs_mode = val & BIT(6); 55662306a36Sopenharmony_ci s->chrgcurr2 = val & BIT(5); 55762306a36Sopenharmony_ci s->chrgcurr1 = val & BIT(4); 55862306a36Sopenharmony_ci s->vbusvld = val & BIT(3); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS4, &val); 56162306a36Sopenharmony_ci if (error) 56262306a36Sopenharmony_ci return error; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci s->battdetb = val & BIT(6); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int cpcap_charger_voltage_to_regval(int voltage) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int offset; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci switch (voltage) { 57462306a36Sopenharmony_ci case 0 ... 4100000 - 1: 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci case 4100000 ... 4200000 - 1: 57762306a36Sopenharmony_ci offset = 1; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case 4200000 ... 4300000 - 1: 58062306a36Sopenharmony_ci offset = 0; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci case 4300000 ... 4380000 - 1: 58362306a36Sopenharmony_ci offset = -1; 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci case 4380000 ... 4440000: 58662306a36Sopenharmony_ci offset = -2; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci default: 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return ((voltage - 4100000) / 20000) + offset; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata, 59662306a36Sopenharmony_ci int state, unsigned long delay) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci int error; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Update battery state before disconnecting the charger */ 60162306a36Sopenharmony_ci switch (state) { 60262306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_DISCHARGING: 60362306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_FULL: 60462306a36Sopenharmony_ci power_supply_changed(ddata->usb); 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci default: 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci error = cpcap_charger_disable(ddata); 61162306a36Sopenharmony_ci if (error) { 61262306a36Sopenharmony_ci cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN); 61362306a36Sopenharmony_ci return; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci cpcap_charger_update_state(ddata, state); 61762306a36Sopenharmony_ci power_supply_changed(ddata->usb); 61862306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, delay); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void cpcap_usb_detect(struct work_struct *work) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata; 62462306a36Sopenharmony_ci struct cpcap_charger_ints_state s; 62562306a36Sopenharmony_ci int error, new_state; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ddata = container_of(work, struct cpcap_charger_ddata, 62862306a36Sopenharmony_ci detect_work.work); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci error = cpcap_charger_get_ints_state(ddata, &s); 63162306a36Sopenharmony_ci if (error) 63262306a36Sopenharmony_ci return; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* Just init the state if a charger is connected with no chrg_det set */ 63562306a36Sopenharmony_ci if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) { 63662306a36Sopenharmony_ci cpcap_charger_update_state(ddata, 63762306a36Sopenharmony_ci POWER_SUPPLY_STATUS_NOT_CHARGING); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* 64362306a36Sopenharmony_ci * If battery voltage is higher than charge voltage, it may have been 64462306a36Sopenharmony_ci * charged to 4.35V by Android. Try again in 10 minutes. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) { 64762306a36Sopenharmony_ci cpcap_charger_disconnect(ddata, 64862306a36Sopenharmony_ci POWER_SUPPLY_STATUS_NOT_CHARGING, 64962306a36Sopenharmony_ci HZ * 60 * 10); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* Delay for 80ms to avoid vbus bouncing when usb cable is plugged in */ 65562306a36Sopenharmony_ci usleep_range(80000, 120000); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Throttle chrgcurr2 interrupt for charger done and retry */ 65862306a36Sopenharmony_ci switch (ddata->status) { 65962306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_CHARGING: 66062306a36Sopenharmony_ci if (s.chrgcurr2) 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci new_state = POWER_SUPPLY_STATUS_FULL; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (s.chrgcurr1 && s.vbusvld) { 66562306a36Sopenharmony_ci cpcap_charger_disconnect(ddata, new_state, HZ * 5); 66662306a36Sopenharmony_ci return; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_FULL: 67062306a36Sopenharmony_ci if (!s.chrgcurr2) 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci if (s.vbusvld) 67362306a36Sopenharmony_ci new_state = POWER_SUPPLY_STATUS_NOT_CHARGING; 67462306a36Sopenharmony_ci else 67562306a36Sopenharmony_ci new_state = POWER_SUPPLY_STATUS_DISCHARGING; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci cpcap_charger_disconnect(ddata, new_state, HZ * 5); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return; 68062306a36Sopenharmony_ci default: 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) && 68562306a36Sopenharmony_ci s.chrgcurr1) { 68662306a36Sopenharmony_ci int max_current; 68762306a36Sopenharmony_ci int vchrg, ichrg; 68862306a36Sopenharmony_ci union power_supply_propval val; 68962306a36Sopenharmony_ci struct power_supply *battery; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci battery = power_supply_get_by_name("battery"); 69262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(battery)) { 69362306a36Sopenharmony_ci dev_err(ddata->dev, "battery power_supply not available %li\n", 69462306a36Sopenharmony_ci PTR_ERR(battery)); 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci error = power_supply_get_property(battery, POWER_SUPPLY_PROP_PRESENT, &val); 69962306a36Sopenharmony_ci power_supply_put(battery); 70062306a36Sopenharmony_ci if (error) 70162306a36Sopenharmony_ci goto out_err; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (val.intval) { 70462306a36Sopenharmony_ci max_current = 1596000; 70562306a36Sopenharmony_ci } else { 70662306a36Sopenharmony_ci dev_info(ddata->dev, "battery not inserted, charging disabled\n"); 70762306a36Sopenharmony_ci max_current = 0; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (max_current > ddata->limit_current) 71162306a36Sopenharmony_ci max_current = ddata->limit_current; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ichrg = cpcap_charger_current_to_regval(max_current); 71462306a36Sopenharmony_ci vchrg = cpcap_charger_voltage_to_regval(ddata->voltage); 71562306a36Sopenharmony_ci error = cpcap_charger_enable(ddata, 71662306a36Sopenharmony_ci CPCAP_REG_CRM_VCHRG(vchrg), 71762306a36Sopenharmony_ci ichrg, 0); 71862306a36Sopenharmony_ci if (error) 71962306a36Sopenharmony_ci goto out_err; 72062306a36Sopenharmony_ci cpcap_charger_update_state(ddata, 72162306a36Sopenharmony_ci POWER_SUPPLY_STATUS_CHARGING); 72262306a36Sopenharmony_ci } else { 72362306a36Sopenharmony_ci error = cpcap_charger_disable(ddata); 72462306a36Sopenharmony_ci if (error) 72562306a36Sopenharmony_ci goto out_err; 72662306a36Sopenharmony_ci cpcap_charger_update_state(ddata, 72762306a36Sopenharmony_ci POWER_SUPPLY_STATUS_DISCHARGING); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci power_supply_changed(ddata->usb); 73162306a36Sopenharmony_ci return; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ciout_err: 73462306a36Sopenharmony_ci cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN); 73562306a36Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic irqreturn_t cpcap_charger_irq_thread(int irq, void *data) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata = data; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (!atomic_read(&ddata->active)) 74362306a36Sopenharmony_ci return IRQ_NONE; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, 0); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return IRQ_HANDLED; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic int cpcap_usb_init_irq(struct platform_device *pdev, 75162306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata, 75262306a36Sopenharmony_ci const char *name) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct cpcap_interrupt_desc *d; 75562306a36Sopenharmony_ci int irq, error; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, name); 75862306a36Sopenharmony_ci if (irq < 0) 75962306a36Sopenharmony_ci return -ENODEV; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci error = devm_request_threaded_irq(ddata->dev, irq, NULL, 76262306a36Sopenharmony_ci cpcap_charger_irq_thread, 76362306a36Sopenharmony_ci IRQF_SHARED | IRQF_ONESHOT, 76462306a36Sopenharmony_ci name, ddata); 76562306a36Sopenharmony_ci if (error) { 76662306a36Sopenharmony_ci dev_err(ddata->dev, "could not get irq %s: %i\n", 76762306a36Sopenharmony_ci name, error); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return error; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci d = devm_kzalloc(ddata->dev, sizeof(*d), GFP_KERNEL); 77362306a36Sopenharmony_ci if (!d) 77462306a36Sopenharmony_ci return -ENOMEM; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci d->name = name; 77762306a36Sopenharmony_ci d->irq = irq; 77862306a36Sopenharmony_ci list_add(&d->node, &ddata->irq_list); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic const char * const cpcap_charger_irqs[] = { 78462306a36Sopenharmony_ci /* REG_INT_0 */ 78562306a36Sopenharmony_ci "chrg_det", "rvrs_chrg", 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* REG_INT1 */ 78862306a36Sopenharmony_ci "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* REG_INT_3 */ 79162306a36Sopenharmony_ci "battdetb", 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int cpcap_usb_init_interrupts(struct platform_device *pdev, 79562306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci int i, error; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cpcap_charger_irqs); i++) { 80062306a36Sopenharmony_ci error = cpcap_usb_init_irq(pdev, ddata, cpcap_charger_irqs[i]); 80162306a36Sopenharmony_ci if (error) 80262306a36Sopenharmony_ci return error; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic void cpcap_charger_init_optional_gpios(struct cpcap_charger_ddata *ddata) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci int i; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 81362306a36Sopenharmony_ci ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode", 81462306a36Sopenharmony_ci i, GPIOD_OUT_HIGH); 81562306a36Sopenharmony_ci if (IS_ERR(ddata->gpio[i])) { 81662306a36Sopenharmony_ci dev_info(ddata->dev, "no mode change GPIO%i: %li\n", 81762306a36Sopenharmony_ci i, PTR_ERR(ddata->gpio[i])); 81862306a36Sopenharmony_ci ddata->gpio[i] = NULL; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int cpcap_charger_init_iio(struct cpcap_charger_ddata *ddata) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci const char * const names[CPCAP_CHARGER_IIO_NR] = { 82662306a36Sopenharmony_ci "battdetb", "battp", "vbus", "chg_isense", "batti", 82762306a36Sopenharmony_ci }; 82862306a36Sopenharmony_ci int error, i; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci for (i = 0; i < CPCAP_CHARGER_IIO_NR; i++) { 83162306a36Sopenharmony_ci ddata->channels[i] = devm_iio_channel_get(ddata->dev, 83262306a36Sopenharmony_ci names[i]); 83362306a36Sopenharmony_ci if (IS_ERR(ddata->channels[i])) { 83462306a36Sopenharmony_ci error = PTR_ERR(ddata->channels[i]); 83562306a36Sopenharmony_ci goto out_err; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!ddata->channels[i]->indio_dev) { 83962306a36Sopenharmony_ci error = -ENXIO; 84062306a36Sopenharmony_ci goto out_err; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ciout_err: 84762306a36Sopenharmony_ci if (error != -EPROBE_DEFER) 84862306a36Sopenharmony_ci dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", 84962306a36Sopenharmony_ci error); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return error; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic char *cpcap_charger_supplied_to[] = { 85562306a36Sopenharmony_ci "battery", 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic const struct power_supply_desc cpcap_charger_usb_desc = { 85962306a36Sopenharmony_ci .name = "usb", 86062306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 86162306a36Sopenharmony_ci .properties = cpcap_charger_props, 86262306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(cpcap_charger_props), 86362306a36Sopenharmony_ci .get_property = cpcap_charger_get_property, 86462306a36Sopenharmony_ci .set_property = cpcap_charger_set_property, 86562306a36Sopenharmony_ci .property_is_writeable = cpcap_charger_property_is_writeable, 86662306a36Sopenharmony_ci}; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci#ifdef CONFIG_OF 86962306a36Sopenharmony_cistatic const struct of_device_id cpcap_charger_id_table[] = { 87062306a36Sopenharmony_ci { 87162306a36Sopenharmony_ci .compatible = "motorola,mapphone-cpcap-charger", 87262306a36Sopenharmony_ci }, 87362306a36Sopenharmony_ci {}, 87462306a36Sopenharmony_ci}; 87562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpcap_charger_id_table); 87662306a36Sopenharmony_ci#endif 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int cpcap_charger_probe(struct platform_device *pdev) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata; 88162306a36Sopenharmony_ci const struct of_device_id *of_id; 88262306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 88362306a36Sopenharmony_ci int error; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci of_id = of_match_device(of_match_ptr(cpcap_charger_id_table), 88662306a36Sopenharmony_ci &pdev->dev); 88762306a36Sopenharmony_ci if (!of_id) 88862306a36Sopenharmony_ci return -EINVAL; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 89162306a36Sopenharmony_ci if (!ddata) 89262306a36Sopenharmony_ci return -ENOMEM; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ddata->dev = &pdev->dev; 89562306a36Sopenharmony_ci ddata->voltage = 4200000; 89662306a36Sopenharmony_ci ddata->limit_current = 532000; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci ddata->reg = dev_get_regmap(ddata->dev->parent, NULL); 89962306a36Sopenharmony_ci if (!ddata->reg) 90062306a36Sopenharmony_ci return -ENODEV; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci INIT_LIST_HEAD(&ddata->irq_list); 90362306a36Sopenharmony_ci INIT_DELAYED_WORK(&ddata->detect_work, cpcap_usb_detect); 90462306a36Sopenharmony_ci INIT_DELAYED_WORK(&ddata->vbus_work, cpcap_charger_vbus_work); 90562306a36Sopenharmony_ci platform_set_drvdata(pdev, ddata); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci error = cpcap_charger_init_iio(ddata); 90862306a36Sopenharmony_ci if (error) 90962306a36Sopenharmony_ci return error; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci atomic_set(&ddata->active, 1); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci psy_cfg.of_node = pdev->dev.of_node; 91462306a36Sopenharmony_ci psy_cfg.drv_data = ddata; 91562306a36Sopenharmony_ci psy_cfg.supplied_to = cpcap_charger_supplied_to; 91662306a36Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(cpcap_charger_supplied_to), 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ddata->usb = devm_power_supply_register(ddata->dev, 91962306a36Sopenharmony_ci &cpcap_charger_usb_desc, 92062306a36Sopenharmony_ci &psy_cfg); 92162306a36Sopenharmony_ci if (IS_ERR(ddata->usb)) { 92262306a36Sopenharmony_ci error = PTR_ERR(ddata->usb); 92362306a36Sopenharmony_ci dev_err(ddata->dev, "failed to register USB charger: %i\n", 92462306a36Sopenharmony_ci error); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return error; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci error = cpcap_usb_init_interrupts(pdev, ddata); 93062306a36Sopenharmony_ci if (error) 93162306a36Sopenharmony_ci return error; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci ddata->comparator.set_vbus = cpcap_charger_set_vbus; 93462306a36Sopenharmony_ci error = omap_usb2_set_comparator(&ddata->comparator); 93562306a36Sopenharmony_ci if (error == -ENODEV) { 93662306a36Sopenharmony_ci dev_info(ddata->dev, "charger needs phy, deferring probe\n"); 93762306a36Sopenharmony_ci return -EPROBE_DEFER; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci cpcap_charger_init_optional_gpios(ddata); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, 0); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci return 0; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic void cpcap_charger_shutdown(struct platform_device *pdev) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct cpcap_charger_ddata *ddata = platform_get_drvdata(pdev); 95062306a36Sopenharmony_ci int error; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci atomic_set(&ddata->active, 0); 95362306a36Sopenharmony_ci error = omap_usb2_set_comparator(NULL); 95462306a36Sopenharmony_ci if (error) 95562306a36Sopenharmony_ci dev_warn(ddata->dev, "could not clear USB comparator: %i\n", 95662306a36Sopenharmony_ci error); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci error = cpcap_charger_disable(ddata); 95962306a36Sopenharmony_ci if (error) { 96062306a36Sopenharmony_ci cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN); 96162306a36Sopenharmony_ci dev_warn(ddata->dev, "could not clear charger: %i\n", 96262306a36Sopenharmony_ci error); 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_DISCHARGING); 96562306a36Sopenharmony_ci cancel_delayed_work_sync(&ddata->vbus_work); 96662306a36Sopenharmony_ci cancel_delayed_work_sync(&ddata->detect_work); 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int cpcap_charger_remove(struct platform_device *pdev) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci cpcap_charger_shutdown(pdev); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic struct platform_driver cpcap_charger_driver = { 97762306a36Sopenharmony_ci .probe = cpcap_charger_probe, 97862306a36Sopenharmony_ci .driver = { 97962306a36Sopenharmony_ci .name = "cpcap-charger", 98062306a36Sopenharmony_ci .of_match_table = of_match_ptr(cpcap_charger_id_table), 98162306a36Sopenharmony_ci }, 98262306a36Sopenharmony_ci .shutdown = cpcap_charger_shutdown, 98362306a36Sopenharmony_ci .remove = cpcap_charger_remove, 98462306a36Sopenharmony_ci}; 98562306a36Sopenharmony_cimodule_platform_driver(cpcap_charger_driver); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ciMODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); 98862306a36Sopenharmony_ciMODULE_DESCRIPTION("CPCAP Battery Charger Interface driver"); 98962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 99062306a36Sopenharmony_ciMODULE_ALIAS("platform:cpcap-charger"); 991