brintos

brintos / linux-shallow public Read only

0
0
Text · 13.9 KiB · 652c1f2 Raw
562 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * DA9150 Fuel-Gauge Driver4 *5 * Copyright (c) 2015 Dialog Semiconductor6 *7 * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>8 */9 10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/of.h>14#include <linux/slab.h>15#include <linux/interrupt.h>16#include <linux/delay.h>17#include <linux/power_supply.h>18#include <linux/list.h>19#include <asm/div64.h>20#include <linux/mfd/da9150/core.h>21#include <linux/mfd/da9150/registers.h>22#include <linux/devm-helpers.h>23 24/* Core2Wire */25#define DA9150_QIF_READ		(0x0 << 7)26#define DA9150_QIF_WRITE	(0x1 << 7)27#define DA9150_QIF_CODE_MASK	0x7F28 29#define DA9150_QIF_BYTE_SIZE	830#define DA9150_QIF_BYTE_MASK	0xFF31#define DA9150_QIF_SHORT_SIZE	232#define DA9150_QIF_LONG_SIZE	433 34/* QIF Codes */35#define DA9150_QIF_UAVG			636#define DA9150_QIF_UAVG_SIZE		DA9150_QIF_LONG_SIZE37#define DA9150_QIF_IAVG			838#define DA9150_QIF_IAVG_SIZE		DA9150_QIF_LONG_SIZE39#define DA9150_QIF_NTCAVG		1240#define DA9150_QIF_NTCAVG_SIZE		DA9150_QIF_LONG_SIZE41#define DA9150_QIF_SHUNT_VAL		3642#define DA9150_QIF_SHUNT_VAL_SIZE	DA9150_QIF_SHORT_SIZE43#define DA9150_QIF_SD_GAIN		3844#define DA9150_QIF_SD_GAIN_SIZE		DA9150_QIF_LONG_SIZE45#define DA9150_QIF_FCC_MAH		4046#define DA9150_QIF_FCC_MAH_SIZE		DA9150_QIF_SHORT_SIZE47#define DA9150_QIF_SOC_PCT		4348#define DA9150_QIF_SOC_PCT_SIZE		DA9150_QIF_SHORT_SIZE49#define DA9150_QIF_CHARGE_LIMIT		4450#define DA9150_QIF_CHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE51#define DA9150_QIF_DISCHARGE_LIMIT	4552#define DA9150_QIF_DISCHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE53#define DA9150_QIF_FW_MAIN_VER		11854#define DA9150_QIF_FW_MAIN_VER_SIZE	DA9150_QIF_SHORT_SIZE55#define DA9150_QIF_E_FG_STATUS		12656#define DA9150_QIF_E_FG_STATUS_SIZE	DA9150_QIF_SHORT_SIZE57#define DA9150_QIF_SYNC			12758#define DA9150_QIF_SYNC_SIZE		DA9150_QIF_SHORT_SIZE59#define DA9150_QIF_MAX_CODES		12860 61/* QIF Sync Timeout */62#define DA9150_QIF_SYNC_TIMEOUT		100063#define DA9150_QIF_SYNC_RETRIES		1064 65/* QIF E_FG_STATUS */66#define DA9150_FG_IRQ_LOW_SOC_MASK	(1 << 0)67#define DA9150_FG_IRQ_HIGH_SOC_MASK	(1 << 1)68#define DA9150_FG_IRQ_SOC_MASK	\69	(DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)70 71/* Private data */72struct da9150_fg {73	struct da9150 *da9150;74	struct device *dev;75 76	struct mutex io_lock;77 78	struct power_supply *battery;79	struct delayed_work work;80	u32 interval;81 82	int warn_soc;83	int crit_soc;84	int soc;85};86 87/* Battery Properties */88static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)89 90{91	u8 buf[DA9150_QIF_LONG_SIZE];92	u8 read_addr;93	u32 res = 0;94	int i;95 96	/* Set QIF code (READ mode) */97	read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;98 99	da9150_read_qif(fg->da9150, read_addr, size, buf);100	for (i = 0; i < size; ++i)101		res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));102 103	return res;104}105 106static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,107				 u32 val)108 109{110	u8 buf[DA9150_QIF_LONG_SIZE];111	u8 write_addr;112	int i;113 114	/* Set QIF code (WRITE mode) */115	write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;116 117	for (i = 0; i < size; ++i) {118		buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &119			 DA9150_QIF_BYTE_MASK;120	}121	da9150_write_qif(fg->da9150, write_addr, size, buf);122}123 124/* Trigger QIF Sync to update QIF readable data */125static void da9150_fg_read_sync_start(struct da9150_fg *fg)126{127	int i = 0;128	u32 res = 0;129 130	mutex_lock(&fg->io_lock);131 132	/* Check if QIF sync already requested, and write to sync if not */133	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,134				  DA9150_QIF_SYNC_SIZE);135	if (res > 0)136		da9150_fg_write_attr(fg, DA9150_QIF_SYNC,137				     DA9150_QIF_SYNC_SIZE, 0);138 139	/* Wait for sync to complete */140	res = 0;141	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {142		usleep_range(DA9150_QIF_SYNC_TIMEOUT,143			     DA9150_QIF_SYNC_TIMEOUT * 2);144		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,145					  DA9150_QIF_SYNC_SIZE);146	}147 148	/* Check if sync completed */149	if (res == 0)150		dev_err(fg->dev, "Failed to perform QIF read sync!\n");151}152 153/*154 * Should always be called after QIF sync read has been performed, and all155 * attributes required have been accessed.156 */157static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)158{159	mutex_unlock(&fg->io_lock);160}161 162/* Sync read of single QIF attribute */163static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)164{165	u32 val;166 167	da9150_fg_read_sync_start(fg);168	val = da9150_fg_read_attr(fg, code, size);169	da9150_fg_read_sync_end(fg);170 171	return val;172}173 174/* Wait for QIF Sync, write QIF data and wait for ack */175static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,176				      u32 val)177{178	int i = 0;179	u32 res = 0, sync_val;180 181	mutex_lock(&fg->io_lock);182 183	/* Check if QIF sync already requested */184	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,185				  DA9150_QIF_SYNC_SIZE);186 187	/* Wait for an existing sync to complete */188	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {189		usleep_range(DA9150_QIF_SYNC_TIMEOUT,190			     DA9150_QIF_SYNC_TIMEOUT * 2);191		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,192					  DA9150_QIF_SYNC_SIZE);193	}194 195	if (res == 0) {196		dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");197		mutex_unlock(&fg->io_lock);198		return;199	}200 201	/* Write value for QIF code */202	da9150_fg_write_attr(fg, code, size, val);203 204	/* Wait for write acknowledgment */205	i = 0;206	sync_val = res;207	while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {208		usleep_range(DA9150_QIF_SYNC_TIMEOUT,209			     DA9150_QIF_SYNC_TIMEOUT * 2);210		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,211					  DA9150_QIF_SYNC_SIZE);212	}213 214	mutex_unlock(&fg->io_lock);215 216	/* Check write was actually successful */217	if (res != (sync_val + 1))218		dev_err(fg->dev, "Error performing QIF sync write for code %d\n",219			code);220}221 222/* Power Supply attributes */223static int da9150_fg_capacity(struct da9150_fg *fg,224			      union power_supply_propval *val)225{226	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,227					       DA9150_QIF_SOC_PCT_SIZE);228 229	if (val->intval > 100)230		val->intval = 100;231 232	return 0;233}234 235static int da9150_fg_current_avg(struct da9150_fg *fg,236				 union power_supply_propval *val)237{238	u32 iavg, sd_gain, shunt_val;239	u64 div, res;240 241	da9150_fg_read_sync_start(fg);242	iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,243				   DA9150_QIF_IAVG_SIZE);244	shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,245					DA9150_QIF_SHUNT_VAL_SIZE);246	sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,247				      DA9150_QIF_SD_GAIN_SIZE);248	da9150_fg_read_sync_end(fg);249 250	div = (u64) (sd_gain * shunt_val * 65536ULL);251	do_div(div, 1000000);252	res = (u64) (iavg * 1000000ULL);253	do_div(res, div);254 255	val->intval = (int) res;256 257	return 0;258}259 260static int da9150_fg_voltage_avg(struct da9150_fg *fg,261				 union power_supply_propval *val)262{263	u64 res;264 265	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,266					       DA9150_QIF_UAVG_SIZE);267 268	res = (u64) (val->intval * 186ULL);269	do_div(res, 10000);270	val->intval = (int) res;271 272	return 0;273}274 275static int da9150_fg_charge_full(struct da9150_fg *fg,276				 union power_supply_propval *val)277{278	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,279					       DA9150_QIF_FCC_MAH_SIZE);280 281	val->intval = val->intval * 1000;282 283	return 0;284}285 286/*287 * Temperature reading from device is only valid if battery/system provides288 * valid NTC to associated pin of DA9150 chip.289 */290static int da9150_fg_temp(struct da9150_fg *fg,291			  union power_supply_propval *val)292{293	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,294					       DA9150_QIF_NTCAVG_SIZE);295 296	val->intval = (val->intval * 10) / 1048576;297 298	return 0;299}300 301static enum power_supply_property da9150_fg_props[] = {302	POWER_SUPPLY_PROP_CAPACITY,303	POWER_SUPPLY_PROP_CURRENT_AVG,304	POWER_SUPPLY_PROP_VOLTAGE_AVG,305	POWER_SUPPLY_PROP_CHARGE_FULL,306	POWER_SUPPLY_PROP_TEMP,307};308 309static int da9150_fg_get_prop(struct power_supply *psy,310			      enum power_supply_property psp,311			      union power_supply_propval *val)312{313	struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);314	int ret;315 316	switch (psp) {317	case POWER_SUPPLY_PROP_CAPACITY:318		ret = da9150_fg_capacity(fg, val);319		break;320	case POWER_SUPPLY_PROP_CURRENT_AVG:321		ret = da9150_fg_current_avg(fg, val);322		break;323	case POWER_SUPPLY_PROP_VOLTAGE_AVG:324		ret = da9150_fg_voltage_avg(fg, val);325		break;326	case POWER_SUPPLY_PROP_CHARGE_FULL:327		ret = da9150_fg_charge_full(fg, val);328		break;329	case POWER_SUPPLY_PROP_TEMP:330		ret = da9150_fg_temp(fg, val);331		break;332	default:333		ret = -EINVAL;334		break;335	}336 337	return ret;338}339 340/* Repeated SOC check */341static bool da9150_fg_soc_changed(struct da9150_fg *fg)342{343	union power_supply_propval val;344 345	da9150_fg_capacity(fg, &val);346	if (val.intval != fg->soc) {347		fg->soc = val.intval;348		return true;349	}350 351	return false;352}353 354static void da9150_fg_work(struct work_struct *work)355{356	struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);357 358	/* Report if SOC has changed */359	if (da9150_fg_soc_changed(fg))360		power_supply_changed(fg->battery);361 362	schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));363}364 365/* SOC level event configuration */366static void da9150_fg_soc_event_config(struct da9150_fg *fg)367{368	int soc;369 370	soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,371				       DA9150_QIF_SOC_PCT_SIZE);372 373	if (soc > fg->warn_soc) {374		/* If SOC > warn level, set discharge warn level event */375		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,376					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,377					  fg->warn_soc + 1);378	} else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {379		/*380		 * If SOC <= warn level, set discharge crit level event,381		 * and set charge warn level event.382		 */383		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,384					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,385					  fg->crit_soc + 1);386 387		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,388					  DA9150_QIF_CHARGE_LIMIT_SIZE,389					  fg->warn_soc);390	} else if (soc <= fg->crit_soc) {391		/* If SOC <= crit level, set charge crit level event */392		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,393					  DA9150_QIF_CHARGE_LIMIT_SIZE,394					  fg->crit_soc);395	}396}397 398static irqreturn_t da9150_fg_irq(int irq, void *data)399{400	struct da9150_fg *fg = data;401	u32 e_fg_status;402 403	/* Read FG IRQ status info */404	e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,405					  DA9150_QIF_E_FG_STATUS_SIZE);406 407	/* Handle warning/critical threhold events */408	if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)409		da9150_fg_soc_event_config(fg);410 411	/* Clear any FG IRQs */412	da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,413			     DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);414 415	return IRQ_HANDLED;416}417 418static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)419{420	struct device_node *fg_node = dev->of_node;421	struct da9150_fg_pdata *pdata;422 423	pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);424	if (!pdata)425		return NULL;426 427	of_property_read_u32(fg_node, "dlg,update-interval",428			     &pdata->update_interval);429	of_property_read_u8(fg_node, "dlg,warn-soc-level",430			    &pdata->warn_soc_lvl);431	of_property_read_u8(fg_node, "dlg,crit-soc-level",432			    &pdata->crit_soc_lvl);433 434	return pdata;435}436 437static const struct power_supply_desc fg_desc = {438	.name		= "da9150-fg",439	.type		= POWER_SUPPLY_TYPE_BATTERY,440	.properties	= da9150_fg_props,441	.num_properties	= ARRAY_SIZE(da9150_fg_props),442	.get_property	= da9150_fg_get_prop,443};444 445static int da9150_fg_probe(struct platform_device *pdev)446{447	struct device *dev = &pdev->dev;448	struct da9150 *da9150 = dev_get_drvdata(dev->parent);449	struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);450	struct da9150_fg *fg;451	int ver, irq, ret = 0;452 453	fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);454	if (fg == NULL)455		return -ENOMEM;456 457	platform_set_drvdata(pdev, fg);458	fg->da9150 = da9150;459	fg->dev = dev;460 461	mutex_init(&fg->io_lock);462 463	/* Enable QIF */464	da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,465			DA9150_FG_QIF_EN_MASK);466 467	fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);468	if (IS_ERR(fg->battery)) {469		ret = PTR_ERR(fg->battery);470		return ret;471	}472 473	ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,474				  DA9150_QIF_FW_MAIN_VER_SIZE);475	dev_info(dev, "Version: 0x%x\n", ver);476 477	/* Handle DT data if provided */478	if (dev->of_node) {479		fg_pdata = da9150_fg_dt_pdata(dev);480		dev->platform_data = fg_pdata;481	}482 483	/* Handle any pdata provided */484	if (fg_pdata) {485		fg->interval = fg_pdata->update_interval;486 487		if (fg_pdata->warn_soc_lvl > 100)488			dev_warn(dev, "Invalid SOC warning level provided, Ignoring");489		else490			fg->warn_soc = fg_pdata->warn_soc_lvl;491 492		if ((fg_pdata->crit_soc_lvl > 100) ||493		    (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))494			dev_warn(dev, "Invalid SOC critical level provided, Ignoring");495		else496			fg->crit_soc = fg_pdata->crit_soc_lvl;497 498 499	}500 501	/* Configure initial SOC level events */502	da9150_fg_soc_event_config(fg);503 504	/*505	 * If an interval period has been provided then setup repeating506	 * work for reporting data updates.507	 */508	if (fg->interval) {509		ret = devm_delayed_work_autocancel(dev, &fg->work,510						   da9150_fg_work);511		if (ret) {512			dev_err(dev, "Failed to init work\n");513			return ret;514		}515 516		schedule_delayed_work(&fg->work,517				      msecs_to_jiffies(fg->interval));518	}519 520	/* Register IRQ */521	irq = platform_get_irq_byname(pdev, "FG");522	if (irq < 0)523		return irq;524 525	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,526					IRQF_ONESHOT, "FG", fg);527	if (ret) {528		dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);529		return ret;530	}531 532	return 0;533}534 535static int da9150_fg_resume(struct platform_device *pdev)536{537	struct da9150_fg *fg = platform_get_drvdata(pdev);538 539	/*540	 * Trigger SOC check to happen now so as to indicate any value change541	 * since last check before suspend.542	 */543	if (fg->interval)544		flush_delayed_work(&fg->work);545 546	return 0;547}548 549static struct platform_driver da9150_fg_driver = {550	.driver = {551		.name = "da9150-fuel-gauge",552	},553	.probe = da9150_fg_probe,554	.resume = da9150_fg_resume,555};556 557module_platform_driver(da9150_fg_driver);558 559MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");560MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");561MODULE_LICENSE("GPL");562