brintos

brintos / linux-shallow public Read only

0
0
Text · 13.7 KiB · 4186fcd Raw
606 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for LP8727 Micro/Mini USB IC with integrated charger4 *5 *			Copyright (C) 2011 Texas Instruments6 *			Copyright (C) 2011 National Semiconductor7 */8 9#include <linux/module.h>10#include <linux/slab.h>11#include <linux/interrupt.h>12#include <linux/i2c.h>13#include <linux/power_supply.h>14#include <linux/platform_data/lp8727.h>15#include <linux/of.h>16 17#define LP8788_NUM_INTREGS	218#define DEFAULT_DEBOUNCE_MSEC	27019 20/* Registers */21#define LP8727_CTRL1		0x122#define LP8727_CTRL2		0x223#define LP8727_SWCTRL		0x324#define LP8727_INT1		0x425#define LP8727_INT2		0x526#define LP8727_STATUS1		0x627#define LP8727_STATUS2		0x728#define LP8727_CHGCTRL2		0x929 30/* CTRL1 register */31#define LP8727_CP_EN		BIT(0)32#define LP8727_ADC_EN		BIT(1)33#define LP8727_ID200_EN		BIT(4)34 35/* CTRL2 register */36#define LP8727_CHGDET_EN	BIT(1)37#define LP8727_INT_EN		BIT(6)38 39/* SWCTRL register */40#define LP8727_SW_DM1_DM	(0x0 << 0)41#define LP8727_SW_DM1_HiZ	(0x7 << 0)42#define LP8727_SW_DP2_DP	(0x0 << 3)43#define LP8727_SW_DP2_HiZ	(0x7 << 3)44 45/* INT1 register */46#define LP8727_IDNO		(0xF << 0)47#define LP8727_VBUS		BIT(4)48 49/* STATUS1 register */50#define LP8727_CHGSTAT		(3 << 4)51#define LP8727_CHPORT		BIT(6)52#define LP8727_DCPORT		BIT(7)53#define LP8727_STAT_EOC		0x3054 55/* STATUS2 register */56#define LP8727_TEMP_STAT	(3 << 5)57#define LP8727_TEMP_SHIFT	558 59/* CHGCTRL2 register */60#define LP8727_ICHG_SHIFT	461 62enum lp8727_dev_id {63	LP8727_ID_NONE,64	LP8727_ID_TA,65	LP8727_ID_DEDICATED_CHG,66	LP8727_ID_USB_CHG,67	LP8727_ID_USB_DS,68	LP8727_ID_MAX,69};70 71enum lp8727_die_temp {72	LP8788_TEMP_75C,73	LP8788_TEMP_95C,74	LP8788_TEMP_115C,75	LP8788_TEMP_135C,76};77 78struct lp8727_psy {79	struct power_supply *ac;80	struct power_supply *usb;81	struct power_supply *batt;82};83 84struct lp8727_chg {85	struct device *dev;86	struct i2c_client *client;87	struct mutex xfer_lock;88	struct lp8727_psy *psy;89	struct lp8727_platform_data *pdata;90 91	/* Charger Data */92	enum lp8727_dev_id devid;93	struct lp8727_chg_param *chg_param;94 95	/* Interrupt Handling */96	int irq;97	struct delayed_work work;98	unsigned long debounce_jiffies;99};100 101static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)102{103	s32 ret;104 105	mutex_lock(&pchg->xfer_lock);106	ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);107	mutex_unlock(&pchg->xfer_lock);108 109	return (ret != len) ? -EIO : 0;110}111 112static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)113{114	return lp8727_read_bytes(pchg, reg, data, 1);115}116 117static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)118{119	int ret;120 121	mutex_lock(&pchg->xfer_lock);122	ret = i2c_smbus_write_byte_data(pchg->client, reg, data);123	mutex_unlock(&pchg->xfer_lock);124 125	return ret;126}127 128static bool lp8727_is_charger_attached(const char *name, int id)129{130	if (!strcmp(name, "ac"))131		return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG;132	else if (!strcmp(name, "usb"))133		return id == LP8727_ID_USB_CHG;134 135	return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG;136}137 138static int lp8727_init_device(struct lp8727_chg *pchg)139{140	u8 val;141	int ret;142	u8 intstat[LP8788_NUM_INTREGS];143 144	/* clear interrupts */145	ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS);146	if (ret)147		return ret;148 149	val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN;150	ret = lp8727_write_byte(pchg, LP8727_CTRL1, val);151	if (ret)152		return ret;153 154	val = LP8727_INT_EN | LP8727_CHGDET_EN;155	return lp8727_write_byte(pchg, LP8727_CTRL2, val);156}157 158static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)159{160	u8 val;161 162	lp8727_read_byte(pchg, LP8727_STATUS1, &val);163	return val & LP8727_DCPORT;164}165 166static int lp8727_is_usb_charger(struct lp8727_chg *pchg)167{168	u8 val;169 170	lp8727_read_byte(pchg, LP8727_STATUS1, &val);171	return val & LP8727_CHPORT;172}173 174static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)175{176	lp8727_write_byte(pchg, LP8727_SWCTRL, sw);177}178 179static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)180{181	struct lp8727_platform_data *pdata = pchg->pdata;182	u8 devid = LP8727_ID_NONE;183	u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ;184 185	switch (id) {186	case 0x5:187		devid = LP8727_ID_TA;188		pchg->chg_param = pdata ? pdata->ac : NULL;189		break;190	case 0xB:191		if (lp8727_is_dedicated_charger(pchg)) {192			pchg->chg_param = pdata ? pdata->ac : NULL;193			devid = LP8727_ID_DEDICATED_CHG;194		} else if (lp8727_is_usb_charger(pchg)) {195			pchg->chg_param = pdata ? pdata->usb : NULL;196			devid = LP8727_ID_USB_CHG;197			swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;198		} else if (vbusin) {199			devid = LP8727_ID_USB_DS;200			swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;201		}202		break;203	default:204		devid = LP8727_ID_NONE;205		pchg->chg_param = NULL;206		break;207	}208 209	pchg->devid = devid;210	lp8727_ctrl_switch(pchg, swctrl);211}212 213static void lp8727_enable_chgdet(struct lp8727_chg *pchg)214{215	u8 val;216 217	lp8727_read_byte(pchg, LP8727_CTRL2, &val);218	val |= LP8727_CHGDET_EN;219	lp8727_write_byte(pchg, LP8727_CTRL2, val);220}221 222static void lp8727_delayed_func(struct work_struct *_work)223{224	struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg,225						work.work);226	u8 intstat[LP8788_NUM_INTREGS];227	u8 idno;228	u8 vbus;229 230	if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) {231		dev_err(pchg->dev, "can not read INT registers\n");232		return;233	}234 235	idno = intstat[0] & LP8727_IDNO;236	vbus = intstat[0] & LP8727_VBUS;237 238	lp8727_id_detection(pchg, idno, vbus);239	lp8727_enable_chgdet(pchg);240 241	power_supply_changed(pchg->psy->ac);242	power_supply_changed(pchg->psy->usb);243	power_supply_changed(pchg->psy->batt);244}245 246static irqreturn_t lp8727_isr_func(int irq, void *ptr)247{248	struct lp8727_chg *pchg = ptr;249 250	schedule_delayed_work(&pchg->work, pchg->debounce_jiffies);251	return IRQ_HANDLED;252}253 254static int lp8727_setup_irq(struct lp8727_chg *pchg)255{256	int ret;257	int irq = pchg->client->irq;258	unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec :259						DEFAULT_DEBOUNCE_MSEC;260 261	INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);262 263	if (irq <= 0) {264		dev_warn(pchg->dev, "invalid irq number: %d\n", irq);265		return 0;266	}267 268	ret = request_threaded_irq(irq,	NULL, lp8727_isr_func,269				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,270				"lp8727_irq", pchg);271 272	if (ret)273		return ret;274 275	pchg->irq = irq;276	pchg->debounce_jiffies = msecs_to_jiffies(delay_msec);277 278	return 0;279}280 281static void lp8727_release_irq(struct lp8727_chg *pchg)282{283	cancel_delayed_work_sync(&pchg->work);284 285	if (pchg->irq)286		free_irq(pchg->irq, pchg);287}288 289static enum power_supply_property lp8727_charger_prop[] = {290	POWER_SUPPLY_PROP_ONLINE,291};292 293static enum power_supply_property lp8727_battery_prop[] = {294	POWER_SUPPLY_PROP_STATUS,295	POWER_SUPPLY_PROP_HEALTH,296	POWER_SUPPLY_PROP_PRESENT,297	POWER_SUPPLY_PROP_VOLTAGE_NOW,298	POWER_SUPPLY_PROP_CAPACITY,299	POWER_SUPPLY_PROP_TEMP,300};301 302static char *battery_supplied_to[] = {303	"main_batt",304};305 306static int lp8727_charger_get_property(struct power_supply *psy,307				       enum power_supply_property psp,308				       union power_supply_propval *val)309{310	struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);311 312	if (psp != POWER_SUPPLY_PROP_ONLINE)313		return -EINVAL;314 315	val->intval = lp8727_is_charger_attached(psy->desc->name, pchg->devid);316 317	return 0;318}319 320static bool lp8727_is_high_temperature(enum lp8727_die_temp temp)321{322	switch (temp) {323	case LP8788_TEMP_95C:324	case LP8788_TEMP_115C:325	case LP8788_TEMP_135C:326		return true;327	default:328		return false;329	}330}331 332static int lp8727_battery_get_property(struct power_supply *psy,333				       enum power_supply_property psp,334				       union power_supply_propval *val)335{336	struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);337	struct lp8727_platform_data *pdata = pchg->pdata;338	enum lp8727_die_temp temp;339	u8 read;340 341	switch (psp) {342	case POWER_SUPPLY_PROP_STATUS:343		if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) {344			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;345			return 0;346		}347 348		lp8727_read_byte(pchg, LP8727_STATUS1, &read);349 350		val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ?351				POWER_SUPPLY_STATUS_FULL :352				POWER_SUPPLY_STATUS_CHARGING;353		break;354	case POWER_SUPPLY_PROP_HEALTH:355		lp8727_read_byte(pchg, LP8727_STATUS2, &read);356		temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT;357 358		val->intval = lp8727_is_high_temperature(temp) ?359			POWER_SUPPLY_HEALTH_OVERHEAT :360			POWER_SUPPLY_HEALTH_GOOD;361		break;362	case POWER_SUPPLY_PROP_PRESENT:363		if (!pdata)364			return -EINVAL;365 366		if (pdata->get_batt_present)367			val->intval = pdata->get_batt_present();368		break;369	case POWER_SUPPLY_PROP_VOLTAGE_NOW:370		if (!pdata)371			return -EINVAL;372 373		if (pdata->get_batt_level)374			val->intval = pdata->get_batt_level();375		break;376	case POWER_SUPPLY_PROP_CAPACITY:377		if (!pdata)378			return -EINVAL;379 380		if (pdata->get_batt_capacity)381			val->intval = pdata->get_batt_capacity();382		break;383	case POWER_SUPPLY_PROP_TEMP:384		if (!pdata)385			return -EINVAL;386 387		if (pdata->get_batt_temp)388			val->intval = pdata->get_batt_temp();389		break;390	default:391		break;392	}393 394	return 0;395}396 397static void lp8727_charger_changed(struct power_supply *psy)398{399	struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);400	u8 eoc_level;401	u8 ichg;402	u8 val;403 404	/* skip if no charger exists */405	if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid))406		return;407 408	/* update charging parameters */409	if (pchg->chg_param) {410		eoc_level = pchg->chg_param->eoc_level;411		ichg = pchg->chg_param->ichg;412		val = (ichg << LP8727_ICHG_SHIFT) | eoc_level;413		lp8727_write_byte(pchg, LP8727_CHGCTRL2, val);414	}415}416 417static const struct power_supply_desc lp8727_ac_desc = {418	.name			= "ac",419	.type			= POWER_SUPPLY_TYPE_MAINS,420	.properties		= lp8727_charger_prop,421	.num_properties		= ARRAY_SIZE(lp8727_charger_prop),422	.get_property		= lp8727_charger_get_property,423};424 425static const struct power_supply_desc lp8727_usb_desc = {426	.name			= "usb",427	.type			= POWER_SUPPLY_TYPE_USB,428	.properties		= lp8727_charger_prop,429	.num_properties		= ARRAY_SIZE(lp8727_charger_prop),430	.get_property		= lp8727_charger_get_property,431};432 433static const struct power_supply_desc lp8727_batt_desc = {434	.name			= "main_batt",435	.type			= POWER_SUPPLY_TYPE_BATTERY,436	.properties		= lp8727_battery_prop,437	.num_properties		= ARRAY_SIZE(lp8727_battery_prop),438	.get_property		= lp8727_battery_get_property,439	.external_power_changed	= lp8727_charger_changed,440};441 442static int lp8727_register_psy(struct lp8727_chg *pchg)443{444	struct power_supply_config psy_cfg = {}; /* Only for ac and usb */445	struct lp8727_psy *psy;446 447	psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL);448	if (!psy)449		return -ENOMEM;450 451	pchg->psy = psy;452 453	psy_cfg.supplied_to = battery_supplied_to;454	psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);455 456	psy->ac = devm_power_supply_register(pchg->dev, &lp8727_ac_desc, &psy_cfg);457	if (IS_ERR(psy->ac))458		return -EPERM;459 460	psy->usb = devm_power_supply_register(pchg->dev, &lp8727_usb_desc,461					      &psy_cfg);462	if (IS_ERR(psy->usb))463		return -EPERM;464 465	psy->batt = devm_power_supply_register(pchg->dev, &lp8727_batt_desc, NULL);466	if (IS_ERR(psy->batt))467		return -EPERM;468 469	return 0;470}471 472#ifdef CONFIG_OF473static struct lp8727_chg_param474*lp8727_parse_charge_pdata(struct device *dev, struct device_node *np)475{476	struct lp8727_chg_param *param;477 478	param = devm_kzalloc(dev, sizeof(*param), GFP_KERNEL);479	if (!param)480		goto out;481 482	of_property_read_u8(np, "eoc-level", (u8 *)&param->eoc_level);483	of_property_read_u8(np, "charging-current", (u8 *)&param->ichg);484out:485	return param;486}487 488static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)489{490	struct device_node *np = dev->of_node;491	struct device_node *child;492	struct lp8727_platform_data *pdata;493	const char *type;494 495	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);496	if (!pdata)497		return ERR_PTR(-ENOMEM);498 499	of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec);500 501	/* If charging parameter is not defined, just skip parsing the dt */502	if (of_get_child_count(np) == 0)503		return pdata;504 505	for_each_child_of_node(np, child) {506		of_property_read_string(child, "charger-type", &type);507 508		if (!strcmp(type, "ac"))509			pdata->ac = lp8727_parse_charge_pdata(dev, child);510 511		if (!strcmp(type, "usb"))512			pdata->usb = lp8727_parse_charge_pdata(dev, child);513	}514 515	return pdata;516}517#else518static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)519{520	return NULL;521}522#endif523 524static int lp8727_probe(struct i2c_client *cl)525{526	struct lp8727_chg *pchg;527	struct lp8727_platform_data *pdata;528	int ret;529 530	if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))531		return -EIO;532 533	if (cl->dev.of_node) {534		pdata = lp8727_parse_dt(&cl->dev);535		if (IS_ERR(pdata))536			return PTR_ERR(pdata);537	} else {538		pdata = dev_get_platdata(&cl->dev);539	}540 541	pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);542	if (!pchg)543		return -ENOMEM;544 545	pchg->client = cl;546	pchg->dev = &cl->dev;547	pchg->pdata = pdata;548	i2c_set_clientdata(cl, pchg);549 550	mutex_init(&pchg->xfer_lock);551 552	ret = lp8727_init_device(pchg);553	if (ret) {554		dev_err(pchg->dev, "i2c communication err: %d", ret);555		return ret;556	}557 558	ret = lp8727_register_psy(pchg);559	if (ret) {560		dev_err(pchg->dev, "power supplies register err: %d", ret);561		return ret;562	}563 564	ret = lp8727_setup_irq(pchg);565	if (ret) {566		dev_err(pchg->dev, "irq handler err: %d", ret);567		return ret;568	}569 570	return 0;571}572 573static void lp8727_remove(struct i2c_client *cl)574{575	struct lp8727_chg *pchg = i2c_get_clientdata(cl);576 577	lp8727_release_irq(pchg);578}579 580static const struct of_device_id lp8727_dt_ids[] __maybe_unused = {581	{ .compatible = "ti,lp8727", },582	{ }583};584MODULE_DEVICE_TABLE(of, lp8727_dt_ids);585 586static const struct i2c_device_id lp8727_ids[] = {587	{ "lp8727" },588	{ }589};590MODULE_DEVICE_TABLE(i2c, lp8727_ids);591 592static struct i2c_driver lp8727_driver = {593	.driver = {594		   .name = "lp8727",595		   .of_match_table = of_match_ptr(lp8727_dt_ids),596		   },597	.probe = lp8727_probe,598	.remove = lp8727_remove,599	.id_table = lp8727_ids,600};601module_i2c_driver(lp8727_driver);602 603MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");604MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");605MODULE_LICENSE("GPL");606