| 22f9dfd by Poly-p man at 2009-02-04 |
1 |
/* |
|
2 |
* Driver for batteries with DS2746 chips inside. |
|
3 |
* |
|
4 |
* Copyright © 2009 Matthew Kern |
|
5 |
* 2007 Anton Vorontsov |
|
6 |
* 2004-2007 Matt Reimer |
|
7 |
* 2004 Szabolcs Gyurko |
|
8 |
* |
|
9 |
* Use consistent with the GNU GPL is permitted, |
|
10 |
* provided that this copyright notice is |
|
11 |
* preserved in its entirety in all copies and derived works. |
|
12 |
* |
|
13 |
* Author: Matthew Kern <pyrophobicman@gmail.com |
|
14 |
* January 2009 |
|
15 |
* |
|
16 |
* Anton Vorontsov <cbou@mail.ru> |
|
17 |
* February 2007 |
|
18 |
* |
|
19 |
* Matt Reimer <mreimer@vpop.net> |
|
20 |
* April 2004, 2005, 2007 |
|
21 |
* |
|
22 |
* Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> |
|
23 |
* September 2004 |
| 94e25f3 by Martin Johnson at 2009-02-10 |
24 |
* |
|
25 |
* Martin Johnson <m.j.johnson@massey.ac.nz> |
|
26 |
* February 2009 - Simplified for HTC Kaiser |
| de40228 by root at 2009-03-19 |
27 |
* |
|
28 |
* Stefan Seidel <kaiser@stefanseidel.info> |
|
29 |
* March 2009 - Correct battery charge pctg. calculation |
| 22f9dfd by Poly-p man at 2009-02-04 |
30 |
*/ |
|
31 |
|
|
32 |
/* I was originally writing this as a generic ds2746 driver, but there are too many kaiser-specific things, so... no. */ |
|
33 |
|
|
34 |
#include <linux/module.h> |
|
35 |
#include <linux/param.h> |
|
36 |
#include <linux/jiffies.h> |
|
37 |
#include <linux/workqueue.h> |
|
38 |
#include <linux/pm.h> |
|
39 |
#include <linux/power_supply.h> |
|
40 |
#include <linux/i2c.h> |
|
41 |
#include <asm/gpio.h> |
|
42 |
|
|
43 |
#ifdef CONFIG_ANDROID_POWER |
|
44 |
#include <linux/android_power.h> |
|
45 |
static android_suspend_lock_t vbus_suspend_lock; |
|
46 |
#endif |
|
47 |
|
|
48 |
|
|
49 |
/* definitions for registers we care about. */ |
|
50 |
#define DS2746_DATA_SIZE 0x12 |
|
51 |
|
|
52 |
#define DS2746_STATUS_REG 0x01 |
| de40228 by root at 2009-03-19 |
53 |
#define DS2746_AUX0_MSB 0x08 |
|
54 |
#define DS2746_AUX0_LSB 0x09 |
|
55 |
#define DS2746_AUX1_MSB 0x0a |
|
56 |
#define DS2746_AUX1_LSB 0x0b |
| 22f9dfd by Poly-p man at 2009-02-04 |
57 |
#define DS2746_VOLTAGE_MSB 0x0c |
|
58 |
#define DS2746_VOLTAGE_LSB 0x0d |
|
59 |
#define DS2746_CURRENT_MSB 0x0e |
|
60 |
#define DS2746_CURRENT_LSB 0x0f |
|
61 |
#define DS2746_CURRENT_ACCUM_MSB 0x10 |
|
62 |
#define DS2746_CURRENT_ACCUM_LSB 0x11 |
|
63 |
|
| de40228 by root at 2009-03-19 |
64 |
#define KAISER_RSNS 10 //in mOHM. From the maths, this should be correct |
| 51149f4 by Biktor at 2009-03-24 |
65 |
#define KAISER_BATTERY_RATING 2700 //in mAh - the standard battery (KAIS160) is this) |
| 22f9dfd by Poly-p man at 2009-02-04 |
66 |
|
|
67 |
|
| 94e25f3 by Martin Johnson at 2009-02-10 |
68 |
struct i2c_client *pclient=0; |
| 22f9dfd by Poly-p man at 2009-02-04 |
69 |
|
| de40228 by root at 2009-03-19 |
70 |
static int battery_capacity = KAISER_BATTERY_RATING; |
|
71 |
module_param(battery_capacity, int, 0644); |
|
72 |
MODULE_PARM_DESC(battery_capacity, "Estimated battery capacity in mAh"); |
|
73 |
|
| 94e25f3 by Martin Johnson at 2009-02-10 |
74 |
static int i2c_read(int r) { |
|
75 |
unsigned char i2c_msg[1]; |
|
76 |
unsigned char i2c_data[2]; |
|
77 |
i2c_msg[0]=r; |
|
78 |
i2c_master_send(pclient, i2c_msg, 1); |
|
79 |
i2c_master_recv(pclient, i2c_data, 2); |
| 3851066 by Martin Johnson at 2009-02-11 |
80 |
// printk("ds2746_i2c_read(%x)=%x %x\n",r,i2c_data[0],i2c_data[1]); |
| 94e25f3 by Martin Johnson at 2009-02-10 |
81 |
return i2c_data[0]; |
| 22f9dfd by Poly-p man at 2009-02-04 |
82 |
} |
|
83 |
|
| de40228 by root at 2009-03-19 |
84 |
static void i2c_write(int r, int v) { |
|
85 |
unsigned char i2c_msg[3]; |
|
86 |
i2c_msg[0]=r; |
|
87 |
i2c_msg[1]=v>>8; |
|
88 |
i2c_msg[2]=v&0xFF; |
|
89 |
i2c_master_send(pclient, i2c_msg, 3); |
|
90 |
} |
|
91 |
|
|
92 |
static int reading2capacity(int r) { |
|
93 |
return (625L * r) / (100L * KAISER_RSNS); |
|
94 |
} |
|
95 |
|
|
96 |
static int capacity2reading(int c) { |
|
97 |
return (100L * c * KAISER_RSNS) / 625; |
|
98 |
} |
|
99 |
|
| 94e25f3 by Martin Johnson at 2009-02-10 |
100 |
struct battery_info_reply { |
|
101 |
u32 batt_id; /* Battery ID from ADC */ |
|
102 |
u32 batt_vol; /* Battery voltage from ADC */ |
|
103 |
u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ |
| 3851066 by Martin Johnson at 2009-02-11 |
104 |
int batt_current; /* Battery current from ADC */ |
| 94e25f3 by Martin Johnson at 2009-02-10 |
105 |
u32 level; /* formula */ |
|
106 |
u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ |
|
107 |
u32 charging_enabled; /* 0: Disable, 1: Enable */ |
|
108 |
u32 full_bat; /* Full capacity of battery (mAh) */ |
| 33c5692 by Martin Johnson at 2009-02-10 |
109 |
}; |
| 22f9dfd by Poly-p man at 2009-02-04 |
110 |
|
| 94e25f3 by Martin Johnson at 2009-02-10 |
111 |
int ds2746_battery_read_status(struct battery_info_reply *b) |
| 22f9dfd by Poly-p man at 2009-02-04 |
112 |
{ |
| 3851066 by Martin Johnson at 2009-02-11 |
113 |
short s; |
| de40228 by root at 2009-03-19 |
114 |
int aux0,aux1; |
|
115 |
int aux0r,aux1r; |
| 22f9dfd by Poly-p man at 2009-02-04 |
116 |
|
| 21895dc by Martin Johnson at 2009-02-24 |
117 |
// printk("read battery status\n"); |
| 94e25f3 by Martin Johnson at 2009-02-10 |
118 |
if(!pclient) { |
|
119 |
printk("client is null\n"); |
|
120 |
b->level=50; |
|
121 |
return 0; |
| 22f9dfd by Poly-p man at 2009-02-04 |
122 |
} |
|
123 |
|
| 3851066 by Martin Johnson at 2009-02-11 |
124 |
s=i2c_read(DS2746_VOLTAGE_LSB) | i2c_read(DS2746_VOLTAGE_MSB)<<8; |
|
125 |
b->batt_vol=((s>>4)*2440)/1000; |
| 22f9dfd by Poly-p man at 2009-02-04 |
126 |
|
| 3851066 by Martin Johnson at 2009-02-11 |
127 |
s=i2c_read(DS2746_CURRENT_LSB) | i2c_read(DS2746_CURRENT_MSB)<<8; |
|
128 |
b->batt_current=((s>>2)*6250) / (KAISER_RSNS*1000); |
| 22f9dfd by Poly-p man at 2009-02-04 |
129 |
|
| de40228 by root at 2009-03-19 |
130 |
/* if battery voltage is < 3.3V and depleting, we assume it's almost empty! */ |
|
131 |
if (b->batt_vol < 3300 && b->batt_current < 0) { |
|
132 |
/* use approximate formula: 3.3V=5%, 3.0V=0% */ |
|
133 |
i2c_write(DS2746_CURRENT_ACCUM_MSB, 1L*battery_capacity*KAISER_RSNS*((5*b->batt_vol)/3-5000)/62500); |
|
134 |
} |
|
135 |
|
| 3851066 by Martin Johnson at 2009-02-11 |
136 |
s=i2c_read(DS2746_CURRENT_ACCUM_LSB) | i2c_read(DS2746_CURRENT_ACCUM_MSB)<<8; |
| de40228 by root at 2009-03-19 |
137 |
|
|
138 |
if (s > 0) { |
|
139 |
int max_seen_capacity = capacity2reading(battery_capacity); |
|
140 |
if (s > max_seen_capacity) { |
|
141 |
/* if the battery is "fuller" than expected, |
|
142 |
* update our expectations */ |
|
143 |
battery_capacity = reading2capacity(s); |
|
144 |
} else { |
|
145 |
/* check if we're >4.2V and <5mA */ |
|
146 |
if ((b->batt_vol >= 4200) && (b->batt_current < 5)) { |
|
147 |
/* that's almost full, so let's assume it is |
|
148 |
* (but leave 10mAh space to the max) */ |
|
149 |
battery_capacity = reading2capacity(s) + 10; |
|
150 |
} |
|
151 |
} |
|
152 |
} |
|
153 |
|
|
154 |
// level = 100 * (s * 6.25 / (BATTERY_RATING * KAISER_RSNS)) |
|
155 |
b->level=(s * 625) / (battery_capacity * KAISER_RSNS); |
|
156 |
/* if we read 0%, */ |
|
157 |
if(b->level<1) { |
|
158 |
/* only report 0% if we're really down, <3.1V */ |
|
159 |
b->level=((b->batt_vol <= 3100 )?0:1); |
|
160 |
} |
|
161 |
|
| 94e25f3 by Martin Johnson at 2009-02-10 |
162 |
if(b->level>100) b->level=100; |
|
163 |
b->full_bat = 100; |
| 22f9dfd by Poly-p man at 2009-02-04 |
164 |
|
| de40228 by root at 2009-03-19 |
165 |
aux0 = i2c_read(DS2746_AUX0_LSB)>>4 | i2c_read(DS2746_AUX0_MSB)<<4; |
|
166 |
aux1 = i2c_read(DS2746_AUX1_LSB)>>4 | i2c_read(DS2746_AUX1_MSB)<<4; |
|
167 |
// ratio |
|
168 |
aux0r = (1000L*aux0)/2047; |
|
169 |
aux1r = (1000L*aux1)/2047; |
|
170 |
// resistance in Ohm (R_AUX=10kOhm) |
|
171 |
aux0r = (10000L*aux0r)/(1000-aux0r); |
|
172 |
aux1r = (10000L*aux1r)/(1000-aux1r); |
|
173 |
|
|
174 |
printk("ds2746: %dmV %dmA charge: %d/100 (%d units)\n aux0: %d (%d) aux1: %d (%d)\n",b->batt_vol,b->batt_current,b->level,s,aux0,aux0r,aux1,aux1r); |
| 22f9dfd by Poly-p man at 2009-02-04 |
175 |
return 0; |
|
176 |
} |
|
177 |
|
| 6e4f45b by Martin Johnson at 2009-02-05 |
178 |
static int ds2746_probe(struct i2c_client *client) |
| 22f9dfd by Poly-p man at 2009-02-04 |
179 |
{ |
|
180 |
|
| 94e25f3 by Martin Johnson at 2009-02-10 |
181 |
printk("DS-2746 Probe\n"); |
|
182 |
pclient = client; |
| 22f9dfd by Poly-p man at 2009-02-04 |
183 |
return 0; |
|
184 |
} |
| 94e25f3 by Martin Johnson at 2009-02-10 |
185 |
|
| 22f9dfd by Poly-p man at 2009-02-04 |
186 |
static int ds2746_detach_client(struct i2c_client *client) |
|
187 |
{ |
|
188 |
i2c_detach_client(client); |
|
189 |
return 0; |
|
190 |
} |
|
191 |
|
| 6e4f45b by Martin Johnson at 2009-02-05 |
192 |
|
|
193 |
static struct i2c_driver ds2746_battery_driver = { |
|
194 |
.probe = ds2746_probe, |
|
195 |
.remove = ds2746_detach_client, |
| 94e25f3 by Martin Johnson at 2009-02-10 |
196 |
.suspend = NULL, |
|
197 |
.resume = NULL, |
|
198 |
.driver = { |
|
199 |
.name = "ds2746-battery", |
| 22f9dfd by Poly-p man at 2009-02-04 |
200 |
}, |
|
201 |
}; |
|
202 |
|
|
203 |
static int __init ds2746_battery_init(void) |
|
204 |
{ |
| 94e25f3 by Martin Johnson at 2009-02-10 |
205 |
|
| 22f9dfd by Poly-p man at 2009-02-04 |
206 |
#ifdef CONFIG_ANDROID_POWER |
|
207 |
vbus_suspend_lock.name = "vbus_present"; |
|
208 |
android_init_suspend_lock(&vbus_suspend_lock); |
|
209 |
#endif |
|
210 |
return i2c_add_driver(&ds2746_battery_driver); |
|
211 |
} |
|
212 |
|
|
213 |
static void __exit ds2746_battery_exit(void) |
|
214 |
{ |
|
215 |
i2c_del_driver(&ds2746_battery_driver); |
|
216 |
} |
|
217 |
|
|
218 |
module_init(ds2746_battery_init); |
|
219 |
module_exit(ds2746_battery_exit); |
|
220 |
|
|
221 |
MODULE_LICENSE("GPL"); |
|
222 |
MODULE_AUTHOR("Matthew Kern <pyrophobicman@gmail.com>, " |
|
223 |
"Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " |
|
224 |
"Matt Reimer <mreimer@vpop.net>, " |
|
225 |
"Anton Vorontsov <cbou@mail.ru>"); |
|
226 |
MODULE_DESCRIPTION("Battery driver for HTC kaiser"); |