162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * BQ27xxx battery monitor HDQ/1-wire driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2007-2017 Texas Instruments Incorporated - https://www.ti.com/
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/power/bq27xxx_battery.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/w1.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define W1_FAMILY_BQ27000	0x01
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define HDQ_CMD_READ	(0 << 7)
2262306a36Sopenharmony_ci#define HDQ_CMD_WRITE	(1 << 7)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int F_ID;
2562306a36Sopenharmony_cimodule_param(F_ID, int, S_IRUSR);
2662306a36Sopenharmony_ciMODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device");
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int w1_bq27000_read(struct w1_slave *sl, unsigned int reg)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	u8 val;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	mutex_lock(&sl->master->bus_mutex);
3362306a36Sopenharmony_ci	w1_write_8(sl->master, HDQ_CMD_READ | reg);
3462306a36Sopenharmony_ci	val = w1_read_8(sl->master);
3562306a36Sopenharmony_ci	mutex_unlock(&sl->master->bus_mutex);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return val;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg,
4162306a36Sopenharmony_ci				    bool single)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct w1_slave *sl = dev_to_w1_slave(di->dev);
4462306a36Sopenharmony_ci	unsigned int timeout = 3;
4562306a36Sopenharmony_ci	int upper, lower;
4662306a36Sopenharmony_ci	int temp;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (!single) {
4962306a36Sopenharmony_ci		/*
5062306a36Sopenharmony_ci		 * Make sure the value has not changed in between reading the
5162306a36Sopenharmony_ci		 * lower and the upper part
5262306a36Sopenharmony_ci		 */
5362306a36Sopenharmony_ci		upper = w1_bq27000_read(sl, reg + 1);
5462306a36Sopenharmony_ci		do {
5562306a36Sopenharmony_ci			temp = upper;
5662306a36Sopenharmony_ci			if (upper < 0)
5762306a36Sopenharmony_ci				return upper;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci			lower = w1_bq27000_read(sl, reg);
6062306a36Sopenharmony_ci			if (lower < 0)
6162306a36Sopenharmony_ci				return lower;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci			upper = w1_bq27000_read(sl, reg + 1);
6462306a36Sopenharmony_ci		} while (temp != upper && --timeout);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		if (timeout == 0)
6762306a36Sopenharmony_ci			return -EIO;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		return (upper << 8) | lower;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return w1_bq27000_read(sl, reg);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct bq27xxx_device_info *di;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL);
8062306a36Sopenharmony_ci	if (!di)
8162306a36Sopenharmony_ci		return -ENOMEM;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	dev_set_drvdata(&sl->dev, di);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	di->dev = &sl->dev;
8662306a36Sopenharmony_ci	di->chip = BQ27000;
8762306a36Sopenharmony_ci	di->name = "bq27000-battery";
8862306a36Sopenharmony_ci	di->bus.read = bq27xxx_battery_hdq_read;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return bq27xxx_battery_setup(di);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	bq27xxx_battery_teardown(di);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic const struct w1_family_ops bq27xxx_battery_hdq_fops = {
10162306a36Sopenharmony_ci	.add_slave	= bq27xxx_battery_hdq_add_slave,
10262306a36Sopenharmony_ci	.remove_slave	= bq27xxx_battery_hdq_remove_slave,
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic struct w1_family bq27xxx_battery_hdq_family = {
10662306a36Sopenharmony_ci	.fid = W1_FAMILY_BQ27000,
10762306a36Sopenharmony_ci	.fops = &bq27xxx_battery_hdq_fops,
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int __init bq27xxx_battery_hdq_init(void)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	if (F_ID)
11362306a36Sopenharmony_ci		bq27xxx_battery_hdq_family.fid = F_ID;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return w1_register_family(&bq27xxx_battery_hdq_family);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_cimodule_init(bq27xxx_battery_hdq_init);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void __exit bq27xxx_battery_hdq_exit(void)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	w1_unregister_family(&bq27xxx_battery_hdq_family);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_cimodule_exit(bq27xxx_battery_hdq_exit);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Ltd");
12662306a36Sopenharmony_ciMODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver");
12762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
12862306a36Sopenharmony_ciMODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000));
129