18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * BQ27xxx battery monitor HDQ/1-wire driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2007-2017 Texas Instruments Incorporated - https://www.ti.com/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/power/bq27xxx_battery.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/w1.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define W1_FAMILY_BQ27000	0x01
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define HDQ_CMD_READ	(0 << 7)
228c2ecf20Sopenharmony_ci#define HDQ_CMD_WRITE	(1 << 7)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int F_ID;
258c2ecf20Sopenharmony_cimodule_param(F_ID, int, S_IRUSR);
268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device");
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int w1_bq27000_read(struct w1_slave *sl, unsigned int reg)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	u8 val;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	mutex_lock(&sl->master->bus_mutex);
338c2ecf20Sopenharmony_ci	w1_write_8(sl->master, HDQ_CMD_READ | reg);
348c2ecf20Sopenharmony_ci	val = w1_read_8(sl->master);
358c2ecf20Sopenharmony_ci	mutex_unlock(&sl->master->bus_mutex);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	return val;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg,
418c2ecf20Sopenharmony_ci				    bool single)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct w1_slave *sl = dev_to_w1_slave(di->dev);
448c2ecf20Sopenharmony_ci	unsigned int timeout = 3;
458c2ecf20Sopenharmony_ci	int upper, lower;
468c2ecf20Sopenharmony_ci	int temp;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (!single) {
498c2ecf20Sopenharmony_ci		/*
508c2ecf20Sopenharmony_ci		 * Make sure the value has not changed in between reading the
518c2ecf20Sopenharmony_ci		 * lower and the upper part
528c2ecf20Sopenharmony_ci		 */
538c2ecf20Sopenharmony_ci		upper = w1_bq27000_read(sl, reg + 1);
548c2ecf20Sopenharmony_ci		do {
558c2ecf20Sopenharmony_ci			temp = upper;
568c2ecf20Sopenharmony_ci			if (upper < 0)
578c2ecf20Sopenharmony_ci				return upper;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci			lower = w1_bq27000_read(sl, reg);
608c2ecf20Sopenharmony_ci			if (lower < 0)
618c2ecf20Sopenharmony_ci				return lower;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci			upper = w1_bq27000_read(sl, reg + 1);
648c2ecf20Sopenharmony_ci		} while (temp != upper && --timeout);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		if (timeout == 0)
678c2ecf20Sopenharmony_ci			return -EIO;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		return (upper << 8) | lower;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return w1_bq27000_read(sl, reg);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct bq27xxx_device_info *di;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL);
808c2ecf20Sopenharmony_ci	if (!di)
818c2ecf20Sopenharmony_ci		return -ENOMEM;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	dev_set_drvdata(&sl->dev, di);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	di->dev = &sl->dev;
868c2ecf20Sopenharmony_ci	di->chip = BQ27000;
878c2ecf20Sopenharmony_ci	di->name = "bq27000-battery";
888c2ecf20Sopenharmony_ci	di->bus.read = bq27xxx_battery_hdq_read;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return bq27xxx_battery_setup(di);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	bq27xxx_battery_teardown(di);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic const struct w1_family_ops bq27xxx_battery_hdq_fops = {
1018c2ecf20Sopenharmony_ci	.add_slave	= bq27xxx_battery_hdq_add_slave,
1028c2ecf20Sopenharmony_ci	.remove_slave	= bq27xxx_battery_hdq_remove_slave,
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic struct w1_family bq27xxx_battery_hdq_family = {
1068c2ecf20Sopenharmony_ci	.fid = W1_FAMILY_BQ27000,
1078c2ecf20Sopenharmony_ci	.fops = &bq27xxx_battery_hdq_fops,
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int __init bq27xxx_battery_hdq_init(void)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (F_ID)
1138c2ecf20Sopenharmony_ci		bq27xxx_battery_hdq_family.fid = F_ID;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return w1_register_family(&bq27xxx_battery_hdq_family);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_cimodule_init(bq27xxx_battery_hdq_init);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void __exit bq27xxx_battery_hdq_exit(void)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	w1_unregister_family(&bq27xxx_battery_hdq_family);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_cimodule_exit(bq27xxx_battery_hdq_exit);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Ltd");
1268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver");
1278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1288c2ecf20Sopenharmony_ciMODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000));
129