1 /*
2 * Copyright (C) 2015 Texas Instruments Inc
3 *
4 * Aneesh V <aneesh@ti.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 #include "bqt.h"
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <linux/i2c.h>
23 #include <linux/i2c-dev.h>
24 #include <sys/ioctl.h>
25 #include <fcntl.h>
27 #define I2C_BUS 2
28 #define I2C_DEV_FILE_BASE "/dev/i2c-"
29 #define I2C_SLAVE_ADDR_DEFUALT 0xAA
30 #define SYSFS_POLL_INTVL "/sys/module/bq27x00_battery/parameters/poll_interval"
33 struct i2c_info_t {
34 int i2c_file;
35 int drvr_poll_intvl;
36 };
38 static int linux_i2c_read(struct gauge_info_t *gauge, uint8_t slave_addr, uint8_t *buf, uint8_t len)
39 {
40 int ret;
41 struct i2c_rdwr_ioctl_data i2c_data;
42 /* msg[0] for write command and msg[1] for read command */
43 struct i2c_msg msgs[2];
44 uint8_t reg = buf[0];
45 int i2c_file = ((struct i2c_info_t *)(gauge->interface))->i2c_file;
47 /*
48 * Write part
49 */
50 /* Linux expects 7 bit slave address */
51 msgs[0].addr = slave_addr >> 1;
52 /* buf[0] contains reg address */
53 msgs[0].buf = buf;
54 msgs[0].flags = 0;
55 msgs[0].len = 1;
57 /*
58 * Read part
59 */
60 msgs[1].addr = msgs[0].addr;
61 /* data starts from buf[1] */
62 msgs[1].buf = &buf[1];
63 msgs[1].flags = I2C_M_RD;
64 msgs[1].len = len;
66 i2c_data.nmsgs = 2;
67 i2c_data.msgs = msgs;
68 ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
69 if (ret < 0) {
70 fprintf(stderr, "I2C read failed reg %02x len %d err %d\n",
71 reg, len, ret);
72 return 0;
73 }
75 return 1;
76 }
78 static int linux_i2c_write(struct gauge_info_t *gauge, uint8_t slave_addr, uint8_t *buf, uint8_t len)
79 {
80 int ret;
81 struct i2c_rdwr_ioctl_data i2c_data;
82 struct i2c_msg msgs[1];
83 int i2c_file = ((struct i2c_info_t *)(gauge->interface))->i2c_file;
85 msgs[0].addr = slave_addr >> 1;
86 /* reg address is part of buf */
87 msgs[0].buf = buf;
88 msgs[0].flags = 0;
89 msgs[0].len = len + 1;
91 i2c_data.nmsgs = 1;
92 i2c_data.msgs = msgs;
93 ret = ioctl(i2c_file, I2C_RDWR, &i2c_data);
94 if (ret < 0) {
95 fprintf(stderr, "I2C write failed slave %02x reg %02x len %d err %d\n",
96 msgs[0].addr, buf[0], len, ret);
97 return 0;
98 }
100 return 1;
101 }
103 static int read_bq_poll_intvl(void)
104 {
105 int poll_file = -1;
106 int poll_intvl = -1;
107 char buf[20];
109 poll_file = open(SYSFS_POLL_INTVL, O_RDONLY);
111 if ((poll_file >= 0) && read(poll_file, buf, 20)) {
112 sscanf(buf, "%d", &poll_intvl);
113 pr_dbg("gauge driver poll interval %ds\n", poll_intvl);
114 } else {
115 pr_err("Failed to read %s\n", SYSFS_POLL_INTVL);
116 }
118 if (poll_file >= 0)
119 close(poll_file);
121 return poll_intvl;
122 }
124 static int write_bq_poll_intvl(int poll_intvl)
125 {
126 int poll_file = -1;
127 char buf[20];
128 int old_poll_intvl, new_poll_intvl;
130 old_poll_intvl = read_bq_poll_intvl();
132 if (old_poll_intvl == poll_intvl) {
133 pr_info("polling interval already %ds\n", poll_intvl);
134 return 1;
135 }
138 poll_file = open(SYSFS_POLL_INTVL, O_RDWR);
140 if (poll_file >= 0) {
141 sprintf(buf, "%d", poll_intvl);
142 write(poll_file, buf, 20);
143 close(poll_file);
144 }
146 new_poll_intvl = read_bq_poll_intvl();
148 if ((poll_file < 0) || (new_poll_intvl != poll_intvl)) {
149 pr_err("failed to set gauge driver polling intvl to %d\n", poll_intvl);
150 return 0;
151 } else {
152 pr_info("changed polling interval from %ds to %ds\n",
153 old_poll_intvl, poll_intvl);
154 return 1;
155 }
156 }
158 static int lock_gauge_interface_linux(struct gauge_info_t *gauge)
159 {
160 struct i2c_info_t *i2c = gauge->interface;
162 pr_dbg(">>\n");
163 i2c->drvr_poll_intvl = read_bq_poll_intvl();
165 /*
166 * Turn off polling. This may not be enough to silence the
167 * driver. We may have to expose a new sysfs file to completely
168 * lock the driver out from accessing the gauge.
169 */
170 if ((i2c->drvr_poll_intvl > 0) && !write_bq_poll_intvl(0)) {
171 pr_err("Failed to stop driver polling, may cause"
172 " communication errors during command execution..\n");
173 return 0;
174 }
176 return 1;
177 }
179 static int unlock_gauge_interface_linux(struct gauge_info_t *gauge)
180 {
181 struct i2c_info_t *i2c = gauge->interface;
183 /* Turn ON polling */
184 if ((i2c->drvr_poll_intvl > 0) &&
185 !write_bq_poll_intvl(i2c->drvr_poll_intvl)) {
186 pr_err("Failed to restore driver polling\n");
187 return 0;
188 }
190 return 1;
191 }
193 static int init_linux_i2c_interface(struct gauge_info_t *gauge, int argc, char **argv)
194 {
195 const char *i2c_dev_file, *i2c_bus, *slave_addr;
196 char *tmp;
197 int ret = 0;
198 char buf[100];
199 struct i2c_info_t *i2c;
201 pr_dbg(">>\n");
203 i2c = (struct i2c_info_t *) malloc(sizeof(struct i2c_info_t));
204 if (!i2c)
205 goto end;
207 i2c_dev_file = get_cmdline_argument(argc, argv, "--i2c-dev-file=", 1);
208 if (!i2c_dev_file) {
209 i2c_bus = get_cmdline_argument(argc, argv, "--i2c-bus=", 1);
210 if (i2c_bus) {
211 snprintf(buf, 100, "%s%s", I2C_DEV_FILE_BASE, i2c_bus);
212 } else {
213 snprintf(buf, 100, "%s%d", I2C_DEV_FILE_BASE, I2C_BUS);
214 pr_err("I2C dev file not specified - assuming %s\n", buf);
215 }
216 i2c_dev_file = buf;
217 }
220 i2c->i2c_file = open(i2c_dev_file, O_RDWR);
221 if (i2c->i2c_file < 0) {
222 pr_err("Failed to open I2C device %s\n", i2c_dev_file);
223 goto end;
224 }
226 slave_addr = get_cmdline_argument(argc, argv, "--slave-addr=", 1);
227 if (slave_addr) {
228 gauge->slave_addr = (uint8_t) strtoul(slave_addr, &tmp, 16);
229 } else {
230 pr_err("slave address not provided, assuming 0x%02x\n",
231 I2C_SLAVE_ADDR_DEFUALT);
232 gauge->slave_addr = I2C_SLAVE_ADDR_DEFUALT;
233 }
235 gauge->interface = i2c;
236 ret = 1;
237 end:
238 if (!ret)
239 pr_err("failed to initialize I2C interface\n");
241 pr_dbg("<<\n");
242 return ret;
243 }
245 static void close_linux_i2c_interface(struct gauge_info_t *gauge)
246 {
247 struct i2c_info_t *i2c = gauge->interface;
249 close(i2c->i2c_file);
251 free(i2c);
253 gauge->interface = NULL;
254 }
256 static void sleep_ms(uint16_t ms)
257 {
258 usleep(ms * 1000);
259 }
261 int setup_comm_callbacks(struct gauge_info_t *gauge)
262 {
263 gauge->init_comm_interface = init_linux_i2c_interface;
264 gauge->close_comm_interface = close_linux_i2c_interface;
265 gauge->lock_gauge_interface = lock_gauge_interface_linux;
266 gauge->unlock_gauge_interface = unlock_gauge_interface_linux;
267 gauge->read = linux_i2c_read;
268 gauge->write = linux_i2c_write;
269 gauge->sleep_ms = sleep_ms;
271 return 1;
272 }