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