brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · 51122bf Raw
661 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Power supply driver for the Active-semi ACT8945A PMIC4 *5 * Copyright (C) 2015 Atmel Corporation6 *7 * Author: Wenyou Yang <wenyou.yang@atmel.com>8 */9#include <linux/interrupt.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_irq.h>13#include <linux/platform_device.h>14#include <linux/power_supply.h>15#include <linux/regmap.h>16#include <linux/gpio/consumer.h>17 18static const char *act8945a_charger_model = "ACT8945A";19static const char *act8945a_charger_manufacturer = "Active-semi";20 21/*22 * ACT8945A Charger Register Map23 */24 25/* 0x70: Reserved */26#define ACT8945A_APCH_CFG		0x7127#define ACT8945A_APCH_STATUS		0x7828#define ACT8945A_APCH_CTRL		0x7929#define ACT8945A_APCH_STATE		0x7A30 31/* ACT8945A_APCH_CFG */32#define APCH_CFG_OVPSET			(0x3 << 0)33#define APCH_CFG_OVPSET_6V6		(0x0 << 0)34#define APCH_CFG_OVPSET_7V		(0x1 << 0)35#define APCH_CFG_OVPSET_7V5		(0x2 << 0)36#define APCH_CFG_OVPSET_8V		(0x3 << 0)37#define APCH_CFG_PRETIMO		(0x3 << 2)38#define APCH_CFG_PRETIMO_40_MIN		(0x0 << 2)39#define APCH_CFG_PRETIMO_60_MIN		(0x1 << 2)40#define APCH_CFG_PRETIMO_80_MIN		(0x2 << 2)41#define APCH_CFG_PRETIMO_DISABLED	(0x3 << 2)42#define APCH_CFG_TOTTIMO		(0x3 << 4)43#define APCH_CFG_TOTTIMO_3_HOUR		(0x0 << 4)44#define APCH_CFG_TOTTIMO_4_HOUR		(0x1 << 4)45#define APCH_CFG_TOTTIMO_5_HOUR		(0x2 << 4)46#define APCH_CFG_TOTTIMO_DISABLED	(0x3 << 4)47#define APCH_CFG_SUSCHG			(0x1 << 7)48 49#define APCH_STATUS_CHGDAT		BIT(0)50#define APCH_STATUS_INDAT		BIT(1)51#define APCH_STATUS_TEMPDAT		BIT(2)52#define APCH_STATUS_TIMRDAT		BIT(3)53#define APCH_STATUS_CHGSTAT		BIT(4)54#define APCH_STATUS_INSTAT		BIT(5)55#define APCH_STATUS_TEMPSTAT		BIT(6)56#define APCH_STATUS_TIMRSTAT		BIT(7)57 58#define APCH_CTRL_CHGEOCOUT		BIT(0)59#define APCH_CTRL_INDIS			BIT(1)60#define APCH_CTRL_TEMPOUT		BIT(2)61#define APCH_CTRL_TIMRPRE		BIT(3)62#define APCH_CTRL_CHGEOCIN		BIT(4)63#define APCH_CTRL_INCON			BIT(5)64#define APCH_CTRL_TEMPIN		BIT(6)65#define APCH_CTRL_TIMRTOT		BIT(7)66 67#define APCH_STATE_ACINSTAT		(0x1 << 1)68#define APCH_STATE_CSTATE		(0x3 << 4)69#define APCH_STATE_CSTATE_SHIFT		470#define APCH_STATE_CSTATE_DISABLED	0x0071#define APCH_STATE_CSTATE_EOC		0x0172#define APCH_STATE_CSTATE_FAST		0x0273#define APCH_STATE_CSTATE_PRE		0x0374 75struct act8945a_charger {76	struct power_supply *psy;77	struct power_supply_desc desc;78	struct regmap *regmap;79	struct work_struct work;80 81	bool init_done;82	struct gpio_desc *lbo_gpio;83	struct gpio_desc *chglev_gpio;84};85 86static int act8945a_get_charger_state(struct regmap *regmap, int *val)87{88	int ret;89	unsigned int status, state;90 91	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);92	if (ret < 0)93		return ret;94 95	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);96	if (ret < 0)97		return ret;98 99	state &= APCH_STATE_CSTATE;100	state >>= APCH_STATE_CSTATE_SHIFT;101 102	switch (state) {103	case APCH_STATE_CSTATE_PRE:104	case APCH_STATE_CSTATE_FAST:105		*val = POWER_SUPPLY_STATUS_CHARGING;106		break;107	case APCH_STATE_CSTATE_EOC:108		if (status & APCH_STATUS_CHGDAT)109			*val = POWER_SUPPLY_STATUS_FULL;110		else111			*val = POWER_SUPPLY_STATUS_CHARGING;112		break;113	case APCH_STATE_CSTATE_DISABLED:114	default:115		if (!(status & APCH_STATUS_INDAT))116			*val = POWER_SUPPLY_STATUS_DISCHARGING;117		else118			*val = POWER_SUPPLY_STATUS_NOT_CHARGING;119		break;120	}121 122	return 0;123}124 125static int act8945a_get_charge_type(struct regmap *regmap, int *val)126{127	int ret;128	unsigned int status, state;129 130	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);131	if (ret < 0)132		return ret;133 134	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);135	if (ret < 0)136		return ret;137 138	state &= APCH_STATE_CSTATE;139	state >>= APCH_STATE_CSTATE_SHIFT;140 141	switch (state) {142	case APCH_STATE_CSTATE_PRE:143		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;144		break;145	case APCH_STATE_CSTATE_FAST:146		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;147		break;148	case APCH_STATE_CSTATE_EOC:149		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;150		break;151	case APCH_STATE_CSTATE_DISABLED:152	default:153		if (!(status & APCH_STATUS_INDAT))154			*val = POWER_SUPPLY_CHARGE_TYPE_NONE;155		else156			*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;157		break;158	}159 160	return 0;161}162 163static int act8945a_get_battery_health(struct regmap *regmap, int *val)164{165	int ret;166	unsigned int status, state, config;167 168	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);169	if (ret < 0)170		return ret;171 172	ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config);173	if (ret < 0)174		return ret;175 176	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);177	if (ret < 0)178		return ret;179 180	state &= APCH_STATE_CSTATE;181	state >>= APCH_STATE_CSTATE_SHIFT;182 183	switch (state) {184	case APCH_STATE_CSTATE_DISABLED:185		if (config & APCH_CFG_SUSCHG) {186			*val = POWER_SUPPLY_HEALTH_UNKNOWN;187		} else if (status & APCH_STATUS_INDAT) {188			if (!(status & APCH_STATUS_TEMPDAT))189				*val = POWER_SUPPLY_HEALTH_OVERHEAT;190			else if (status & APCH_STATUS_TIMRDAT)191				*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;192			else193				*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;194		} else {195			*val = POWER_SUPPLY_HEALTH_GOOD;196		}197		break;198	case APCH_STATE_CSTATE_PRE:199	case APCH_STATE_CSTATE_FAST:200	case APCH_STATE_CSTATE_EOC:201	default:202		*val = POWER_SUPPLY_HEALTH_GOOD;203		break;204	}205 206	return 0;207}208 209static int act8945a_get_capacity_level(struct act8945a_charger *charger,210				       struct regmap *regmap, int *val)211{212	int ret;213	unsigned int status, state, config;214	int lbo_level = gpiod_get_value(charger->lbo_gpio);215 216	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);217	if (ret < 0)218		return ret;219 220	ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config);221	if (ret < 0)222		return ret;223 224	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);225	if (ret < 0)226		return ret;227 228	state &= APCH_STATE_CSTATE;229	state >>= APCH_STATE_CSTATE_SHIFT;230 231	switch (state) {232	case APCH_STATE_CSTATE_PRE:233		*val = POWER_SUPPLY_CAPACITY_LEVEL_LOW;234		break;235	case APCH_STATE_CSTATE_FAST:236		if (lbo_level)237			*val = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;238		else239			*val = POWER_SUPPLY_CAPACITY_LEVEL_LOW;240		break;241	case APCH_STATE_CSTATE_EOC:242		if (status & APCH_STATUS_CHGDAT)243			*val = POWER_SUPPLY_CAPACITY_LEVEL_FULL;244		else245			*val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;246		break;247	case APCH_STATE_CSTATE_DISABLED:248	default:249		if (config & APCH_CFG_SUSCHG) {250			*val = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;251		} else {252			*val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;253			if (!(status & APCH_STATUS_INDAT)) {254				if (!lbo_level)255					*val = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;256			}257		}258		break;259	}260 261	return 0;262}263 264#define MAX_CURRENT_USB_HIGH	450000265#define MAX_CURRENT_USB_LOW	90000266#define MAX_CURRENT_USB_PRE	45000267/*268 * Riset(K) = 2336 * (1V/Ichg(mA)) - 0.205269 * Riset = 2.43K270 */271#define MAX_CURRENT_AC_HIGH		886527272#define MAX_CURRENT_AC_LOW		117305273#define MAX_CURRENT_AC_HIGH_PRE		88653274#define MAX_CURRENT_AC_LOW_PRE		11731275 276static int act8945a_get_current_max(struct act8945a_charger *charger,277				    struct regmap *regmap, int *val)278{279	int ret;280	unsigned int status, state;281	unsigned int acin_state;282	int chgin_level = gpiod_get_value(charger->chglev_gpio);283 284	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);285	if (ret < 0)286		return ret;287 288	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);289	if (ret < 0)290		return ret;291 292	acin_state = (state & APCH_STATE_ACINSTAT) >> 1;293 294	state &= APCH_STATE_CSTATE;295	state >>= APCH_STATE_CSTATE_SHIFT;296 297	switch (state) {298	case APCH_STATE_CSTATE_PRE:299		if (acin_state) {300			if (chgin_level)301				*val = MAX_CURRENT_AC_HIGH_PRE;302			else303				*val = MAX_CURRENT_AC_LOW_PRE;304		} else {305			*val = MAX_CURRENT_USB_PRE;306		}307		break;308	case APCH_STATE_CSTATE_FAST:309		if (acin_state) {310			if (chgin_level)311				*val = MAX_CURRENT_AC_HIGH;312			else313				*val = MAX_CURRENT_AC_LOW;314		} else {315			if (chgin_level)316				*val = MAX_CURRENT_USB_HIGH;317			else318				*val = MAX_CURRENT_USB_LOW;319		}320		break;321	case APCH_STATE_CSTATE_EOC:322	case APCH_STATE_CSTATE_DISABLED:323	default:324		*val = 0;325		break;326	}327 328	return 0;329}330 331static enum power_supply_property act8945a_charger_props[] = {332	POWER_SUPPLY_PROP_STATUS,333	POWER_SUPPLY_PROP_CHARGE_TYPE,334	POWER_SUPPLY_PROP_TECHNOLOGY,335	POWER_SUPPLY_PROP_HEALTH,336	POWER_SUPPLY_PROP_CAPACITY_LEVEL,337	POWER_SUPPLY_PROP_CURRENT_MAX,338	POWER_SUPPLY_PROP_MODEL_NAME,339	POWER_SUPPLY_PROP_MANUFACTURER340};341 342static int act8945a_charger_get_property(struct power_supply *psy,343					 enum power_supply_property prop,344					 union power_supply_propval *val)345{346	struct act8945a_charger *charger = power_supply_get_drvdata(psy);347	struct regmap *regmap = charger->regmap;348	int ret = 0;349 350	switch (prop) {351	case POWER_SUPPLY_PROP_STATUS:352		ret = act8945a_get_charger_state(regmap, &val->intval);353		break;354	case POWER_SUPPLY_PROP_CHARGE_TYPE:355		ret = act8945a_get_charge_type(regmap, &val->intval);356		break;357	case POWER_SUPPLY_PROP_TECHNOLOGY:358		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;359		break;360	case POWER_SUPPLY_PROP_HEALTH:361		ret = act8945a_get_battery_health(regmap, &val->intval);362		break;363	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:364		ret = act8945a_get_capacity_level(charger,365						  regmap, &val->intval);366		break;367	case POWER_SUPPLY_PROP_CURRENT_MAX:368		ret = act8945a_get_current_max(charger,369					       regmap, &val->intval);370		break;371	case POWER_SUPPLY_PROP_MODEL_NAME:372		val->strval = act8945a_charger_model;373		break;374	case POWER_SUPPLY_PROP_MANUFACTURER:375		val->strval = act8945a_charger_manufacturer;376		break;377	default:378		return -EINVAL;379	}380 381	return ret;382}383 384static int act8945a_enable_interrupt(struct act8945a_charger *charger)385{386	struct regmap *regmap = charger->regmap;387	unsigned char ctrl;388	int ret;389 390	ctrl = APCH_CTRL_CHGEOCOUT | APCH_CTRL_CHGEOCIN |391	       APCH_CTRL_INDIS | APCH_CTRL_INCON |392	       APCH_CTRL_TEMPOUT | APCH_CTRL_TEMPIN |393	       APCH_CTRL_TIMRPRE | APCH_CTRL_TIMRTOT;394	ret = regmap_write(regmap, ACT8945A_APCH_CTRL, ctrl);395	if (ret)396		return ret;397 398	ctrl = APCH_STATUS_CHGSTAT | APCH_STATUS_INSTAT |399	       APCH_STATUS_TEMPSTAT | APCH_STATUS_TIMRSTAT;400	ret = regmap_write(regmap, ACT8945A_APCH_STATUS, ctrl);401	if (ret)402		return ret;403 404	return 0;405}406 407static unsigned int act8945a_set_supply_type(struct act8945a_charger *charger,408					     unsigned int *type)409{410	unsigned int status, state;411	int ret;412 413	ret = regmap_read(charger->regmap, ACT8945A_APCH_STATUS, &status);414	if (ret < 0)415		return ret;416 417	ret = regmap_read(charger->regmap, ACT8945A_APCH_STATE, &state);418	if (ret < 0)419		return ret;420 421	if (status & APCH_STATUS_INDAT) {422		if (state & APCH_STATE_ACINSTAT)423			*type = POWER_SUPPLY_TYPE_MAINS;424		else425			*type = POWER_SUPPLY_TYPE_USB;426	} else {427		*type = POWER_SUPPLY_TYPE_BATTERY;428	}429 430	return 0;431}432 433static void act8945a_work(struct work_struct *work)434{435	struct act8945a_charger *charger =436			container_of(work, struct act8945a_charger, work);437 438	act8945a_set_supply_type(charger, &charger->desc.type);439 440	power_supply_changed(charger->psy);441}442 443static irqreturn_t act8945a_status_changed(int irq, void *dev_id)444{445	struct act8945a_charger *charger = dev_id;446 447	if (charger->init_done)448		schedule_work(&charger->work);449 450	return IRQ_HANDLED;451}452 453#define DEFAULT_TOTAL_TIME_OUT		3454#define DEFAULT_PRE_TIME_OUT		40455#define DEFAULT_INPUT_OVP_THRESHOLD	6600456 457static int act8945a_charger_config(struct device *dev,458				   struct act8945a_charger *charger)459{460	struct device_node *np = dev->of_node;461	struct regmap *regmap = charger->regmap;462 463	u32 total_time_out;464	u32 pre_time_out;465	u32 input_voltage_threshold;466	int err, ret;467 468	unsigned int tmp;469	unsigned int value = 0;470 471	if (!np) {472		dev_err(dev, "no charger of node\n");473		return -EINVAL;474	}475 476	ret = regmap_read(regmap, ACT8945A_APCH_CFG, &tmp);477	if (ret)478		return ret;479 480	if (tmp & APCH_CFG_SUSCHG) {481		value |= APCH_CFG_SUSCHG;482		dev_info(dev, "have been suspended\n");483	}484 485	charger->lbo_gpio = devm_gpiod_get_optional(dev, "active-semi,lbo",486						    GPIOD_IN);487	if (IS_ERR(charger->lbo_gpio)) {488		err = PTR_ERR(charger->lbo_gpio);489		dev_err(dev, "unable to claim gpio \"lbo\": %d\n", err);490		return err;491	}492 493	ret = devm_request_irq(dev, gpiod_to_irq(charger->lbo_gpio),494			       act8945a_status_changed,495			       (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),496			       "act8945a_lbo_detect", charger);497	if (ret)498		dev_info(dev, "failed to request gpio \"lbo\" IRQ\n");499 500	charger->chglev_gpio = devm_gpiod_get_optional(dev,501						       "active-semi,chglev",502						       GPIOD_IN);503	if (IS_ERR(charger->chglev_gpio)) {504		err = PTR_ERR(charger->chglev_gpio);505		dev_err(dev, "unable to claim gpio \"chglev\": %d\n", err);506		return err;507	}508 509	if (of_property_read_u32(np,510				 "active-semi,input-voltage-threshold-microvolt",511				 &input_voltage_threshold))512		input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD;513 514	if (of_property_read_u32(np,515				 "active-semi,precondition-timeout",516				 &pre_time_out))517		pre_time_out = DEFAULT_PRE_TIME_OUT;518 519	if (of_property_read_u32(np, "active-semi,total-timeout",520				 &total_time_out))521		total_time_out = DEFAULT_TOTAL_TIME_OUT;522 523	switch (input_voltage_threshold) {524	case 8000:525		value |= APCH_CFG_OVPSET_8V;526		break;527	case 7500:528		value |= APCH_CFG_OVPSET_7V5;529		break;530	case 7000:531		value |= APCH_CFG_OVPSET_7V;532		break;533	case 6600:534	default:535		value |= APCH_CFG_OVPSET_6V6;536		break;537	}538 539	switch (pre_time_out) {540	case 60:541		value |= APCH_CFG_PRETIMO_60_MIN;542		break;543	case 80:544		value |= APCH_CFG_PRETIMO_80_MIN;545		break;546	case 0:547		value |= APCH_CFG_PRETIMO_DISABLED;548		break;549	case 40:550	default:551		value |= APCH_CFG_PRETIMO_40_MIN;552		break;553	}554 555	switch (total_time_out) {556	case 4:557		value |= APCH_CFG_TOTTIMO_4_HOUR;558		break;559	case 5:560		value |= APCH_CFG_TOTTIMO_5_HOUR;561		break;562	case 0:563		value |= APCH_CFG_TOTTIMO_DISABLED;564		break;565	case 3:566	default:567		value |= APCH_CFG_TOTTIMO_3_HOUR;568		break;569	}570 571	return regmap_write(regmap, ACT8945A_APCH_CFG, value);572}573 574static int act8945a_charger_probe(struct platform_device *pdev)575{576	struct act8945a_charger *charger;577	struct power_supply_config psy_cfg = {};578	int irq, ret;579 580	charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);581	if (!charger)582		return -ENOMEM;583 584	charger->regmap = dev_get_regmap(pdev->dev.parent, NULL);585	if (!charger->regmap) {586		dev_err(&pdev->dev, "Parent did not provide regmap\n");587		return -EINVAL;588	}589 590	ret = act8945a_charger_config(&pdev->dev, charger);591	if (ret)592		return ret;593 594	irq = of_irq_get(pdev->dev.of_node, 0);595	if (irq <= 0) {596		dev_err(&pdev->dev, "failed to find IRQ number\n");597		return irq ?: -ENXIO;598	}599 600	ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed,601			       IRQF_TRIGGER_FALLING, "act8945a_interrupt",602			       charger);603	if (ret) {604		dev_err(&pdev->dev, "failed to request nIRQ pin IRQ\n");605		return ret;606	}607 608	charger->desc.name = "act8945a-charger";609	charger->desc.get_property = act8945a_charger_get_property;610	charger->desc.properties = act8945a_charger_props;611	charger->desc.num_properties = ARRAY_SIZE(act8945a_charger_props);612 613	ret = act8945a_set_supply_type(charger, &charger->desc.type);614	if (ret)615		return -EINVAL;616 617	psy_cfg.of_node	= pdev->dev.of_node;618	psy_cfg.drv_data = charger;619 620	charger->psy = devm_power_supply_register(&pdev->dev,621						  &charger->desc,622						  &psy_cfg);623	if (IS_ERR(charger->psy)) {624		dev_err(&pdev->dev, "failed to register power supply\n");625		return PTR_ERR(charger->psy);626	}627 628	platform_set_drvdata(pdev, charger);629 630	INIT_WORK(&charger->work, act8945a_work);631 632	ret = act8945a_enable_interrupt(charger);633	if (ret)634		return -EIO;635 636	charger->init_done = true;637 638	return 0;639}640 641static void act8945a_charger_remove(struct platform_device *pdev)642{643	struct act8945a_charger *charger = platform_get_drvdata(pdev);644 645	charger->init_done = false;646	cancel_work_sync(&charger->work);647}648 649static struct platform_driver act8945a_charger_driver = {650	.driver	= {651		.name = "act8945a-charger",652	},653	.probe	= act8945a_charger_probe,654	.remove_new = act8945a_charger_remove,655};656module_platform_driver(act8945a_charger_driver);657 658MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");659MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");660MODULE_LICENSE("GPL");661