155a9db67SAndrew F. Davis // SPDX-License-Identifier: GPL-2.0
267bd22c0SAndrew F. Davis /*
355a9db67SAndrew F. Davis  * BQ27xxx battery monitor HDQ/1-wire driver
481bd45fcSAlexander A. Klimov  *
555a9db67SAndrew F. Davis  * Copyright (C) 2007-2017 Texas Instruments Incorporated - https://www.ti.com/
667bd22c0SAndrew F. Davis  *
767bd22c0SAndrew F. Davis  */
867bd22c0SAndrew F. Davis 
967bd22c0SAndrew F. Davis #include <linux/kernel.h>
1067bd22c0SAndrew F. Davis #include <linux/module.h>
1167bd22c0SAndrew F. Davis #include <linux/device.h>
1267bd22c0SAndrew F. Davis #include <linux/types.h>
1367bd22c0SAndrew F. Davis #include <linux/platform_device.h>
1455a9db67SAndrew F. Davis #include <linux/mutex.h>
1555a9db67SAndrew F. Davis #include <linux/power/bq27xxx_battery.h>
1655a9db67SAndrew F. Davis 
1755a9db67SAndrew F. Davis #include <linux/w1.h>
1855a9db67SAndrew F. Davis 
1955a9db67SAndrew F. Davis #define W1_FAMILY_BQ27000	0x01
2055a9db67SAndrew F. Davis 
2155a9db67SAndrew F. Davis #define HDQ_CMD_READ	(0 << 7)
2255a9db67SAndrew F. Davis #define HDQ_CMD_WRITE	(1 << 7)
2355a9db67SAndrew F. Davis 
2455a9db67SAndrew F. Davis static int F_ID;
2555a9db67SAndrew F. Davis module_param(F_ID, int, S_IRUSR);
2655a9db67SAndrew F. Davis MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device");
2755a9db67SAndrew F. Davis 
w1_bq27000_read(struct w1_slave * sl,unsigned int reg)2867bd22c0SAndrew F. Davis static int w1_bq27000_read(struct w1_slave *sl, unsigned int reg)
2955a9db67SAndrew F. Davis {
3055a9db67SAndrew F. Davis 	u8 val;
3155a9db67SAndrew F. Davis 
3255a9db67SAndrew F. Davis 	mutex_lock(&sl->master->bus_mutex);
3367bd22c0SAndrew F. Davis 	w1_write_8(sl->master, HDQ_CMD_READ | reg);
3455a9db67SAndrew F. Davis 	val = w1_read_8(sl->master);
3567bd22c0SAndrew F. Davis 	mutex_unlock(&sl->master->bus_mutex);
3655a9db67SAndrew F. Davis 
3755a9db67SAndrew F. Davis 	return val;
3855a9db67SAndrew F. Davis }
3955a9db67SAndrew F. Davis 
bq27xxx_battery_hdq_read(struct bq27xxx_device_info * di,u8 reg,bool single)4055a9db67SAndrew F. Davis static int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg,
4155a9db67SAndrew F. Davis 				    bool single)
4255a9db67SAndrew F. Davis {
4355a9db67SAndrew F. Davis 	struct w1_slave *sl = dev_to_w1_slave(di->dev);
4455a9db67SAndrew F. Davis 	unsigned int timeout = 3;
4555a9db67SAndrew F. Davis 	int upper, lower;
4655a9db67SAndrew F. Davis 	int temp;
4767bd22c0SAndrew F. Davis 
4867bd22c0SAndrew F. Davis 	if (!single) {
4967bd22c0SAndrew F. Davis 		/*
5067bd22c0SAndrew F. Davis 		 * Make sure the value has not changed in between reading the
5167bd22c0SAndrew F. Davis 		 * lower and the upper part
5267bd22c0SAndrew F. Davis 		 */
5367bd22c0SAndrew F. Davis 		upper = w1_bq27000_read(sl, reg + 1);
5467bd22c0SAndrew F. Davis 		do {
5567bd22c0SAndrew F. Davis 			temp = upper;
5667bd22c0SAndrew F. Davis 			if (upper < 0)
5767bd22c0SAndrew F. Davis 				return upper;
5867bd22c0SAndrew F. Davis 
5967bd22c0SAndrew F. Davis 			lower = w1_bq27000_read(sl, reg);
6067bd22c0SAndrew F. Davis 			if (lower < 0)
6167bd22c0SAndrew F. Davis 				return lower;
6267bd22c0SAndrew F. Davis 
6367bd22c0SAndrew F. Davis 			upper = w1_bq27000_read(sl, reg + 1);
6467bd22c0SAndrew F. Davis 		} while (temp != upper && --timeout);
6567bd22c0SAndrew F. Davis 
6667bd22c0SAndrew F. Davis 		if (timeout == 0)
6767bd22c0SAndrew F. Davis 			return -EIO;
6867bd22c0SAndrew F. Davis 
6967bd22c0SAndrew F. Davis 		return (upper << 8) | lower;
7067bd22c0SAndrew F. Davis 	}
7167bd22c0SAndrew F. Davis 
7267bd22c0SAndrew F. Davis 	return w1_bq27000_read(sl, reg);
7367bd22c0SAndrew F. Davis }
7467bd22c0SAndrew F. Davis 
bq27xxx_battery_hdq_add_slave(struct w1_slave * sl)7567bd22c0SAndrew F. Davis static int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl)
7667bd22c0SAndrew F. Davis {
7767bd22c0SAndrew F. Davis 	struct bq27xxx_device_info *di;
7867bd22c0SAndrew F. Davis 
7967bd22c0SAndrew F. Davis 	di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL);
8067bd22c0SAndrew F. Davis 	if (!di)
8167bd22c0SAndrew F. Davis 		return -ENOMEM;
8267bd22c0SAndrew F. Davis 
8367bd22c0SAndrew F. Davis 	dev_set_drvdata(&sl->dev, di);
8467bd22c0SAndrew F. Davis 
8567bd22c0SAndrew F. Davis 	di->dev = &sl->dev;
8667bd22c0SAndrew F. Davis 	di->chip = BQ27000;
8767bd22c0SAndrew F. Davis 	di->name = "bq27000-battery";
8867bd22c0SAndrew F. Davis 	di->bus.read = bq27xxx_battery_hdq_read;
8967bd22c0SAndrew F. Davis 
9067bd22c0SAndrew F. Davis 	return bq27xxx_battery_setup(di);
9167bd22c0SAndrew F. Davis }
9267bd22c0SAndrew F. Davis 
bq27xxx_battery_hdq_remove_slave(struct w1_slave * sl)9367bd22c0SAndrew F. Davis static void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl)
9467bd22c0SAndrew F. Davis {
9567bd22c0SAndrew F. Davis 	struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev);
9667bd22c0SAndrew F. Davis 
9767bd22c0SAndrew F. Davis 	bq27xxx_battery_teardown(di);
9867bd22c0SAndrew F. Davis }
9967bd22c0SAndrew F. Davis 
10067bd22c0SAndrew F. Davis static const struct w1_family_ops bq27xxx_battery_hdq_fops = {
10167bd22c0SAndrew F. Davis 	.add_slave	= bq27xxx_battery_hdq_add_slave,
10267bd22c0SAndrew F. Davis 	.remove_slave	= bq27xxx_battery_hdq_remove_slave,
10367bd22c0SAndrew F. Davis };
10467bd22c0SAndrew F. Davis 
10567bd22c0SAndrew F. Davis static struct w1_family bq27xxx_battery_hdq_family = {
10667bd22c0SAndrew F. Davis 	.fid = W1_FAMILY_BQ27000,
1076925478cSRikard Falkeborn 	.fops = &bq27xxx_battery_hdq_fops,
10867bd22c0SAndrew F. Davis };
10967bd22c0SAndrew F. Davis 
bq27xxx_battery_hdq_init(void)11055a9db67SAndrew F. Davis static int __init bq27xxx_battery_hdq_init(void)
11155a9db67SAndrew F. Davis {
11267bd22c0SAndrew F. Davis 	if (F_ID)
11355a9db67SAndrew F. Davis 		bq27xxx_battery_hdq_family.fid = F_ID;
11467bd22c0SAndrew F. Davis 
11555a9db67SAndrew F. Davis 	return w1_register_family(&bq27xxx_battery_hdq_family);
11655a9db67SAndrew F. Davis }
11767bd22c0SAndrew F. Davis module_init(bq27xxx_battery_hdq_init);
11855a9db67SAndrew F. Davis 
bq27xxx_battery_hdq_exit(void)11955a9db67SAndrew F. Davis static void __exit bq27xxx_battery_hdq_exit(void)
12067bd22c0SAndrew F. Davis {
12155a9db67SAndrew F. Davis 	w1_unregister_family(&bq27xxx_battery_hdq_family);
12267bd22c0SAndrew F. Davis }
12355a9db67SAndrew F. Davis module_exit(bq27xxx_battery_hdq_exit);
12467bd22c0SAndrew F. Davis 
12555a9db67SAndrew F. Davis MODULE_AUTHOR("Texas Instruments Ltd");
12667bd22c0SAndrew F. Davis MODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver");
12755a9db67SAndrew F. Davis MODULE_LICENSE("GPL");
12867bd22c0SAndrew F. Davis MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000));
12955a9db67SAndrew F. Davis