brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · b13cecd Raw
647 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * DA9150 Charger Driver4 *5 * Copyright (c) 2014 Dialog Semiconductor6 *7 * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>8 */9 10#include <linux/kernel.h>11#include <linux/slab.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/interrupt.h>15#include <linux/power_supply.h>16#include <linux/notifier.h>17#include <linux/usb/phy.h>18#include <linux/iio/consumer.h>19#include <linux/mfd/da9150/core.h>20#include <linux/mfd/da9150/registers.h>21 22/* Private data */23struct da9150_charger {24	struct da9150 *da9150;25	struct device *dev;26 27	struct power_supply *usb;28	struct power_supply *battery;29	struct power_supply *supply_online;30 31	struct usb_phy *usb_phy;32	struct notifier_block otg_nb;33	struct work_struct otg_work;34	unsigned long usb_event;35 36	struct iio_channel *ibus_chan;37	struct iio_channel *vbus_chan;38	struct iio_channel *tjunc_chan;39	struct iio_channel *vbat_chan;40};41 42static inline int da9150_charger_supply_online(struct da9150_charger *charger,43					       struct power_supply *psy,44					       union power_supply_propval *val)45{46	val->intval = (psy == charger->supply_online) ? 1 : 0;47 48	return 0;49}50 51/* Charger Properties */52static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger,53					   union power_supply_propval *val)54{55	int v_val, ret;56 57	/* Read processed value - mV units */58	ret = iio_read_channel_processed(charger->vbus_chan, &v_val);59	if (ret < 0)60		return ret;61 62	/* Convert voltage to expected uV units */63	val->intval = v_val * 1000;64 65	return 0;66}67 68static int da9150_charger_ibus_current_avg(struct da9150_charger *charger,69					   union power_supply_propval *val)70{71	int i_val, ret;72 73	/* Read processed value - mA units */74	ret = iio_read_channel_processed(charger->ibus_chan, &i_val);75	if (ret < 0)76		return ret;77 78	/* Convert current to expected uA units */79	val->intval = i_val * 1000;80 81	return 0;82}83 84static int da9150_charger_tjunc_temp(struct da9150_charger *charger,85				     union power_supply_propval *val)86{87	int t_val, ret;88 89	/* Read processed value - 0.001 degrees C units */90	ret = iio_read_channel_processed(charger->tjunc_chan, &t_val);91	if (ret < 0)92		return ret;93 94	/* Convert temp to expect 0.1 degrees C units */95	val->intval = t_val / 100;96 97	return 0;98}99 100static enum power_supply_property da9150_charger_props[] = {101	POWER_SUPPLY_PROP_ONLINE,102	POWER_SUPPLY_PROP_VOLTAGE_NOW,103	POWER_SUPPLY_PROP_CURRENT_AVG,104	POWER_SUPPLY_PROP_TEMP,105};106 107static int da9150_charger_get_prop(struct power_supply *psy,108				   enum power_supply_property psp,109				   union power_supply_propval *val)110{111	struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);112	int ret;113 114	switch (psp) {115	case POWER_SUPPLY_PROP_ONLINE:116		ret = da9150_charger_supply_online(charger, psy, val);117		break;118	case POWER_SUPPLY_PROP_VOLTAGE_NOW:119		ret = da9150_charger_vbus_voltage_now(charger, val);120		break;121	case POWER_SUPPLY_PROP_CURRENT_AVG:122		ret = da9150_charger_ibus_current_avg(charger, val);123		break;124	case POWER_SUPPLY_PROP_TEMP:125		ret = da9150_charger_tjunc_temp(charger, val);126		break;127	default:128		ret = -EINVAL;129		break;130	}131 132	return ret;133}134 135/* Battery Properties */136static int da9150_charger_battery_status(struct da9150_charger *charger,137					 union power_supply_propval *val)138{139	u8 reg;140 141	/* Check to see if battery is discharging */142	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);143 144	if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) ||145	    ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) {146		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;147 148		return 0;149	}150 151	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);152 153	/* Now check for other states */154	switch (reg & DA9150_CHG_STAT_MASK) {155	case DA9150_CHG_STAT_ACT:156	case DA9150_CHG_STAT_PRE:157	case DA9150_CHG_STAT_CC:158	case DA9150_CHG_STAT_CV:159		val->intval = POWER_SUPPLY_STATUS_CHARGING;160		break;161	case DA9150_CHG_STAT_OFF:162	case DA9150_CHG_STAT_SUSP:163	case DA9150_CHG_STAT_TEMP:164	case DA9150_CHG_STAT_TIME:165	case DA9150_CHG_STAT_BAT:166		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;167		break;168	case DA9150_CHG_STAT_FULL:169		val->intval = POWER_SUPPLY_STATUS_FULL;170		break;171	default:172		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;173		break;174	}175 176	return 0;177}178 179static int da9150_charger_battery_health(struct da9150_charger *charger,180					 union power_supply_propval *val)181{182	u8 reg;183 184	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);185 186	/* Check if temperature limit reached */187	switch (reg & DA9150_CHG_TEMP_MASK) {188	case DA9150_CHG_TEMP_UNDER:189		val->intval = POWER_SUPPLY_HEALTH_COLD;190		return 0;191	case DA9150_CHG_TEMP_OVER:192		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;193		return 0;194	default:195		break;196	}197 198	/* Check for other health states */199	switch (reg & DA9150_CHG_STAT_MASK) {200	case DA9150_CHG_STAT_ACT:201	case DA9150_CHG_STAT_PRE:202		val->intval = POWER_SUPPLY_HEALTH_DEAD;203		break;204	case DA9150_CHG_STAT_TIME:205		val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;206		break;207	default:208		val->intval = POWER_SUPPLY_HEALTH_GOOD;209		break;210	}211 212	return 0;213}214 215static int da9150_charger_battery_present(struct da9150_charger *charger,216					  union power_supply_propval *val)217{218	u8 reg;219 220	/* Check if battery present or removed */221	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);222	if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT)223		val->intval = 0;224	else225		val->intval = 1;226 227	return 0;228}229 230static int da9150_charger_battery_charge_type(struct da9150_charger *charger,231					      union power_supply_propval *val)232{233	u8 reg;234 235	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);236 237	switch (reg & DA9150_CHG_STAT_MASK) {238	case DA9150_CHG_STAT_CC:239		val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;240		break;241	case DA9150_CHG_STAT_ACT:242	case DA9150_CHG_STAT_PRE:243	case DA9150_CHG_STAT_CV:244		val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;245		break;246	default:247		val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;248		break;249	}250 251	return 0;252}253 254static int da9150_charger_battery_voltage_min(struct da9150_charger *charger,255					      union power_supply_propval *val)256{257	u8 reg;258 259	reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C);260 261	/* Value starts at 2500 mV, 50 mV increments, presented in uV */262	val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000;263 264	return 0;265}266 267static int da9150_charger_battery_voltage_now(struct da9150_charger *charger,268					      union power_supply_propval *val)269{270	int v_val, ret;271 272	/* Read processed value - mV units */273	ret = iio_read_channel_processed(charger->vbat_chan, &v_val);274	if (ret < 0)275		return ret;276 277	val->intval = v_val * 1000;278 279	return 0;280}281 282static int da9150_charger_battery_current_max(struct da9150_charger *charger,283					      union power_supply_propval *val)284{285	int reg;286 287	reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D);288 289	/* 25mA increments */290	val->intval = reg * 25000;291 292	return 0;293}294 295static int da9150_charger_battery_voltage_max(struct da9150_charger *charger,296					      union power_supply_propval *val)297{298	u8 reg;299 300	reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B);301 302	/* Value starts at 3650 mV, 25 mV increments, presented in uV */303	val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000;304	return 0;305}306 307static enum power_supply_property da9150_charger_bat_props[] = {308	POWER_SUPPLY_PROP_STATUS,309	POWER_SUPPLY_PROP_ONLINE,310	POWER_SUPPLY_PROP_HEALTH,311	POWER_SUPPLY_PROP_PRESENT,312	POWER_SUPPLY_PROP_CHARGE_TYPE,313	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,314	POWER_SUPPLY_PROP_VOLTAGE_NOW,315	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,316	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,317};318 319static int da9150_charger_battery_get_prop(struct power_supply *psy,320					   enum power_supply_property psp,321					   union power_supply_propval *val)322{323	struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);324	int ret;325 326	switch (psp) {327	case POWER_SUPPLY_PROP_STATUS:328		ret = da9150_charger_battery_status(charger, val);329		break;330	case POWER_SUPPLY_PROP_ONLINE:331		ret = da9150_charger_supply_online(charger, psy, val);332		break;333	case POWER_SUPPLY_PROP_HEALTH:334		ret = da9150_charger_battery_health(charger, val);335		break;336	case POWER_SUPPLY_PROP_PRESENT:337		ret = da9150_charger_battery_present(charger, val);338		break;339	case POWER_SUPPLY_PROP_CHARGE_TYPE:340		ret = da9150_charger_battery_charge_type(charger, val);341		break;342	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:343		ret = da9150_charger_battery_voltage_min(charger, val);344		break;345	case POWER_SUPPLY_PROP_VOLTAGE_NOW:346		ret = da9150_charger_battery_voltage_now(charger, val);347		break;348	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:349		ret = da9150_charger_battery_current_max(charger, val);350		break;351	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:352		ret = da9150_charger_battery_voltage_max(charger, val);353		break;354	default:355		ret = -EINVAL;356		break;357	}358 359	return ret;360}361 362static irqreturn_t da9150_charger_chg_irq(int irq, void *data)363{364	struct da9150_charger *charger = data;365 366	power_supply_changed(charger->battery);367 368	return IRQ_HANDLED;369}370 371static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data)372{373	struct da9150_charger *charger = data;374 375	/* Nothing we can really do except report this. */376	dev_crit(charger->dev, "TJunc over temperature!!!\n");377	power_supply_changed(charger->usb);378 379	return IRQ_HANDLED;380}381 382static irqreturn_t da9150_charger_vfault_irq(int irq, void *data)383{384	struct da9150_charger *charger = data;385 386	/* Nothing we can really do except report this. */387	dev_crit(charger->dev, "VSYS under voltage!!!\n");388	power_supply_changed(charger->usb);389	power_supply_changed(charger->battery);390 391	return IRQ_HANDLED;392}393 394static irqreturn_t da9150_charger_vbus_irq(int irq, void *data)395{396	struct da9150_charger *charger = data;397	u8 reg;398 399	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);400 401	/* Charger plugged in or battery only */402	switch (reg & DA9150_VBUS_STAT_MASK) {403	case DA9150_VBUS_STAT_OFF:404	case DA9150_VBUS_STAT_WAIT:405		charger->supply_online = charger->battery;406		break;407	case DA9150_VBUS_STAT_CHG:408		charger->supply_online = charger->usb;409		break;410	default:411		dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n",412			 reg);413		charger->supply_online = NULL;414		break;415	}416 417	power_supply_changed(charger->usb);418	power_supply_changed(charger->battery);419 420	return IRQ_HANDLED;421}422 423static void da9150_charger_otg_work(struct work_struct *data)424{425	struct da9150_charger *charger =426		container_of(data, struct da9150_charger, otg_work);427 428	switch (charger->usb_event) {429	case USB_EVENT_ID:430		/* Enable OTG Boost */431		da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,432				DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG);433		break;434	case USB_EVENT_NONE:435		/* Revert to charge mode */436		power_supply_changed(charger->usb);437		power_supply_changed(charger->battery);438		da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,439				DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG);440		break;441	}442}443 444static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val,445				  void *priv)446{447	struct da9150_charger *charger =448		container_of(nb, struct da9150_charger, otg_nb);449 450	dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val);451 452	charger->usb_event = val;453	schedule_work(&charger->otg_work);454 455	return NOTIFY_OK;456}457 458static int da9150_charger_register_irq(struct platform_device *pdev,459				       irq_handler_t handler,460				       const char *irq_name)461{462	struct device *dev = &pdev->dev;463	struct da9150_charger *charger = platform_get_drvdata(pdev);464	int irq, ret;465 466	irq = platform_get_irq_byname(pdev, irq_name);467	if (irq < 0)468		return irq;469 470	ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name,471				   charger);472	if (ret)473		dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);474 475	return ret;476}477 478static void da9150_charger_unregister_irq(struct platform_device *pdev,479					  const char *irq_name)480{481	struct da9150_charger *charger = platform_get_drvdata(pdev);482	int irq;483 484	irq = platform_get_irq_byname(pdev, irq_name);485	if (irq < 0)486		return;487 488	free_irq(irq, charger);489}490 491static const struct power_supply_desc usb_desc = {492	.name		= "da9150-usb",493	.type		= POWER_SUPPLY_TYPE_USB,494	.properties	= da9150_charger_props,495	.num_properties	= ARRAY_SIZE(da9150_charger_props),496	.get_property	= da9150_charger_get_prop,497};498 499static const struct power_supply_desc battery_desc = {500	.name		= "da9150-battery",501	.type		= POWER_SUPPLY_TYPE_BATTERY,502	.properties	= da9150_charger_bat_props,503	.num_properties	= ARRAY_SIZE(da9150_charger_bat_props),504	.get_property	= da9150_charger_battery_get_prop,505};506 507static int da9150_charger_probe(struct platform_device *pdev)508{509	struct device *dev = &pdev->dev;510	struct da9150 *da9150 = dev_get_drvdata(dev->parent);511	struct da9150_charger *charger;512	u8 reg;513	int ret;514 515	charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL);516	if (!charger)517		return -ENOMEM;518 519	platform_set_drvdata(pdev, charger);520	charger->da9150 = da9150;521	charger->dev = dev;522 523	/* Acquire ADC channels */524	charger->ibus_chan = devm_iio_channel_get(dev, "CHAN_IBUS");525	if (IS_ERR(charger->ibus_chan))526		return PTR_ERR(charger->ibus_chan);527 528	charger->vbus_chan = devm_iio_channel_get(dev, "CHAN_VBUS");529	if (IS_ERR(charger->vbus_chan))530		return PTR_ERR(charger->vbus_chan);531 532	charger->tjunc_chan = devm_iio_channel_get(dev, "CHAN_TJUNC");533	if (IS_ERR(charger->tjunc_chan))534		return PTR_ERR(charger->tjunc_chan);535 536	charger->vbat_chan = devm_iio_channel_get(dev, "CHAN_VBAT");537	if (IS_ERR(charger->vbat_chan))538		return PTR_ERR(charger->vbat_chan);539 540	/* Register power supplies */541	charger->usb = devm_power_supply_register(dev, &usb_desc, NULL);542	if (IS_ERR(charger->usb))543		return PTR_ERR(charger->usb);544 545	charger->battery = devm_power_supply_register(dev, &battery_desc, NULL);546	if (IS_ERR(charger->battery))547		return PTR_ERR(charger->battery);548 549	/* Get initial online supply */550	reg = da9150_reg_read(da9150, DA9150_STATUS_H);551 552	switch (reg & DA9150_VBUS_STAT_MASK) {553	case DA9150_VBUS_STAT_OFF:554	case DA9150_VBUS_STAT_WAIT:555		charger->supply_online = charger->battery;556		break;557	case DA9150_VBUS_STAT_CHG:558		charger->supply_online = charger->usb;559		break;560	default:561		dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg);562		charger->supply_online = NULL;563		break;564	}565 566	/* Setup OTG reporting & configuration */567	charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);568	if (!IS_ERR_OR_NULL(charger->usb_phy)) {569		INIT_WORK(&charger->otg_work, da9150_charger_otg_work);570		charger->otg_nb.notifier_call = da9150_charger_otg_ncb;571		usb_register_notifier(charger->usb_phy, &charger->otg_nb);572	}573 574	/* Register IRQs */575	ret = da9150_charger_register_irq(pdev, da9150_charger_chg_irq,576					  "CHG_STATUS");577	if (ret < 0)578		goto chg_irq_fail;579 580	ret = da9150_charger_register_irq(pdev, da9150_charger_tjunc_irq,581					  "CHG_TJUNC");582	if (ret < 0)583		goto tjunc_irq_fail;584 585	ret = da9150_charger_register_irq(pdev, da9150_charger_vfault_irq,586					  "CHG_VFAULT");587	if (ret < 0)588		goto vfault_irq_fail;589 590	ret = da9150_charger_register_irq(pdev, da9150_charger_vbus_irq,591					  "CHG_VBUS");592	if (ret < 0)593		goto vbus_irq_fail;594 595	return 0;596 597 598vbus_irq_fail:599	da9150_charger_unregister_irq(pdev, "CHG_VFAULT");600vfault_irq_fail:601	da9150_charger_unregister_irq(pdev, "CHG_TJUNC");602tjunc_irq_fail:603	da9150_charger_unregister_irq(pdev, "CHG_STATUS");604chg_irq_fail:605	if (!IS_ERR_OR_NULL(charger->usb_phy))606		usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);607 608	return ret;609}610 611static void da9150_charger_remove(struct platform_device *pdev)612{613	struct da9150_charger *charger = platform_get_drvdata(pdev);614	int irq;615 616	/* Make sure IRQs are released before unregistering power supplies */617	irq = platform_get_irq_byname(pdev, "CHG_VBUS");618	free_irq(irq, charger);619 620	irq = platform_get_irq_byname(pdev, "CHG_VFAULT");621	free_irq(irq, charger);622 623	irq = platform_get_irq_byname(pdev, "CHG_TJUNC");624	free_irq(irq, charger);625 626	irq = platform_get_irq_byname(pdev, "CHG_STATUS");627	free_irq(irq, charger);628 629	if (!IS_ERR_OR_NULL(charger->usb_phy))630		usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);631	cancel_work_sync(&charger->otg_work);632}633 634static struct platform_driver da9150_charger_driver = {635	.driver = {636		.name = "da9150-charger",637	},638	.probe = da9150_charger_probe,639	.remove_new = da9150_charger_remove,640};641 642module_platform_driver(da9150_charger_driver);643 644MODULE_DESCRIPTION("Charger Driver for DA9150");645MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");646MODULE_LICENSE("GPL");647