Commit | Line | Data |
---|---|---|
cfbc6190 | 1 | /* |
67bd22c0 | 2 | * BQ27xxx battery monitor HDQ/1-wire driver |
cfbc6190 | 3 | * |
67bd22c0 | 4 | * Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/ |
cfbc6190 | 5 | * |
67bd22c0 AD |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | * kind, whether express or implied; without even the implied warranty | |
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
cfbc6190 MC |
14 | */ |
15 | ||
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/device.h> | |
19 | #include <linux/types.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/mutex.h> | |
081bab21 | 22 | #include <linux/power/bq27xxx_battery.h> |
cfbc6190 | 23 | |
de0d6dbd AD |
24 | #include <linux/w1.h> |
25 | ||
26 | #define W1_FAMILY_BQ27000 0x01 | |
cfbc6190 | 27 | |
67bd22c0 AD |
28 | #define HDQ_CMD_READ (0 << 7) |
29 | #define HDQ_CMD_WRITE (1 << 7) | |
cfbc6190 MC |
30 | |
31 | static int F_ID; | |
50fa2951 | 32 | module_param(F_ID, int, S_IRUSR); |
67bd22c0 | 33 | MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device"); |
cfbc6190 | 34 | |
67bd22c0 | 35 | static int w1_bq27000_read(struct w1_slave *sl, unsigned int reg) |
cfbc6190 MC |
36 | { |
37 | u8 val; | |
cfbc6190 | 38 | |
b02f8bed | 39 | mutex_lock(&sl->master->bus_mutex); |
cfbc6190 MC |
40 | w1_write_8(sl->master, HDQ_CMD_READ | reg); |
41 | val = w1_read_8(sl->master); | |
b02f8bed | 42 | mutex_unlock(&sl->master->bus_mutex); |
cfbc6190 MC |
43 | |
44 | return val; | |
45 | } | |
9f3519d2 | 46 | |
67bd22c0 AD |
47 | static int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg, |
48 | bool single) | |
cfbc6190 | 49 | { |
67bd22c0 AD |
50 | struct w1_slave *sl = dev_to_w1_slave(di->dev); |
51 | unsigned int timeout = 3; | |
52 | int upper, lower; | |
53 | int temp; | |
54 | ||
55 | if (!single) { | |
56 | /* | |
57 | * Make sure the value has not changed in between reading the | |
58 | * lower and the upper part | |
59 | */ | |
60 | upper = w1_bq27000_read(sl, reg + 1); | |
61 | do { | |
62 | temp = upper; | |
63 | if (upper < 0) | |
64 | return upper; | |
65 | ||
66 | lower = w1_bq27000_read(sl, reg); | |
67 | if (lower < 0) | |
68 | return lower; | |
69 | ||
70 | upper = w1_bq27000_read(sl, reg + 1); | |
71 | } while (temp != upper && --timeout); | |
72 | ||
73 | if (timeout == 0) | |
74 | return -EIO; | |
75 | ||
76 | return (upper << 8) | lower; | |
cfbc6190 | 77 | } |
cfbc6190 | 78 | |
67bd22c0 AD |
79 | return w1_bq27000_read(sl, reg); |
80 | } | |
cfbc6190 | 81 | |
67bd22c0 AD |
82 | static int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl) |
83 | { | |
84 | struct bq27xxx_device_info *di; | |
85 | ||
86 | di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL); | |
87 | if (!di) | |
88 | return -ENOMEM; | |
cfbc6190 | 89 | |
67bd22c0 | 90 | dev_set_drvdata(&sl->dev, di); |
cfbc6190 | 91 | |
67bd22c0 AD |
92 | di->dev = &sl->dev; |
93 | di->chip = BQ27000; | |
94 | di->name = "bq27000-battery"; | |
95 | di->bus.read = bq27xxx_battery_hdq_read; | |
96 | ||
97 | return bq27xxx_battery_setup(di); | |
cfbc6190 MC |
98 | } |
99 | ||
67bd22c0 | 100 | static void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl) |
cfbc6190 | 101 | { |
67bd22c0 | 102 | struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev); |
cfbc6190 | 103 | |
67bd22c0 | 104 | bq27xxx_battery_teardown(di); |
cfbc6190 MC |
105 | } |
106 | ||
67bd22c0 AD |
107 | static struct w1_family_ops bq27xxx_battery_hdq_fops = { |
108 | .add_slave = bq27xxx_battery_hdq_add_slave, | |
109 | .remove_slave = bq27xxx_battery_hdq_remove_slave, | |
cfbc6190 MC |
110 | }; |
111 | ||
67bd22c0 | 112 | static struct w1_family bq27xxx_battery_hdq_family = { |
4b7e4f82 | 113 | .fid = W1_FAMILY_BQ27000, |
67bd22c0 | 114 | .fops = &bq27xxx_battery_hdq_fops, |
cfbc6190 MC |
115 | }; |
116 | ||
67bd22c0 | 117 | static int __init bq27xxx_battery_hdq_init(void) |
cfbc6190 MC |
118 | { |
119 | if (F_ID) | |
67bd22c0 | 120 | bq27xxx_battery_hdq_family.fid = F_ID; |
cfbc6190 | 121 | |
67bd22c0 | 122 | return w1_register_family(&bq27xxx_battery_hdq_family); |
cfbc6190 | 123 | } |
67bd22c0 | 124 | module_init(bq27xxx_battery_hdq_init); |
cfbc6190 | 125 | |
67bd22c0 | 126 | static void __exit bq27xxx_battery_hdq_exit(void) |
cfbc6190 | 127 | { |
67bd22c0 | 128 | w1_unregister_family(&bq27xxx_battery_hdq_family); |
cfbc6190 | 129 | } |
67bd22c0 | 130 | module_exit(bq27xxx_battery_hdq_exit); |
cfbc6190 | 131 | |
cfbc6190 | 132 | MODULE_AUTHOR("Texas Instruments Ltd"); |
67bd22c0 | 133 | MODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver"); |
50fa2951 AD |
134 | MODULE_LICENSE("GPL"); |
135 | MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000)); |