• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Battery measurement code for Zipit Z2
3  *
4  * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com>
5  *
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  */
11 
12 #include <linux/module.h>
13 #include <linux/gpio.h>
14 #include <linux/i2c.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/power_supply.h>
18 #include <linux/slab.h>
19 #include <linux/z2_battery.h>
20 
21 #define	Z2_DEFAULT_NAME	"Z2"
22 
23 struct z2_charger {
24 	struct z2_battery_info		*info;
25 	int				bat_status;
26 	struct i2c_client		*client;
27 	struct power_supply		*batt_ps;
28 	struct power_supply_desc	batt_ps_desc;
29 	struct mutex			work_lock;
30 	struct work_struct		bat_work;
31 };
32 
z2_read_bat(struct z2_charger * charger)33 static unsigned long z2_read_bat(struct z2_charger *charger)
34 {
35 	int data;
36 	data = i2c_smbus_read_byte_data(charger->client,
37 					charger->info->batt_I2C_reg);
38 	if (data < 0)
39 		return 0;
40 
41 	return data * charger->info->batt_mult / charger->info->batt_div;
42 }
43 
z2_batt_get_property(struct power_supply * batt_ps,enum power_supply_property psp,union power_supply_propval * val)44 static int z2_batt_get_property(struct power_supply *batt_ps,
45 			    enum power_supply_property psp,
46 			    union power_supply_propval *val)
47 {
48 	struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
49 	struct z2_battery_info *info = charger->info;
50 
51 	switch (psp) {
52 	case POWER_SUPPLY_PROP_STATUS:
53 		val->intval = charger->bat_status;
54 		break;
55 	case POWER_SUPPLY_PROP_TECHNOLOGY:
56 		val->intval = info->batt_tech;
57 		break;
58 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
59 		if (info->batt_I2C_reg >= 0)
60 			val->intval = z2_read_bat(charger);
61 		else
62 			return -EINVAL;
63 		break;
64 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
65 		if (info->max_voltage >= 0)
66 			val->intval = info->max_voltage;
67 		else
68 			return -EINVAL;
69 		break;
70 	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
71 		if (info->min_voltage >= 0)
72 			val->intval = info->min_voltage;
73 		else
74 			return -EINVAL;
75 		break;
76 	case POWER_SUPPLY_PROP_PRESENT:
77 		val->intval = 1;
78 		break;
79 	default:
80 		return -EINVAL;
81 	}
82 
83 	return 0;
84 }
85 
z2_batt_ext_power_changed(struct power_supply * batt_ps)86 static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
87 {
88 	struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
89 
90 	schedule_work(&charger->bat_work);
91 }
92 
z2_batt_update(struct z2_charger * charger)93 static void z2_batt_update(struct z2_charger *charger)
94 {
95 	int old_status = charger->bat_status;
96 	struct z2_battery_info *info;
97 
98 	info = charger->info;
99 
100 	mutex_lock(&charger->work_lock);
101 
102 	charger->bat_status = (info->charge_gpio >= 0) ?
103 		(gpio_get_value(info->charge_gpio) ?
104 		POWER_SUPPLY_STATUS_CHARGING :
105 		POWER_SUPPLY_STATUS_DISCHARGING) :
106 		POWER_SUPPLY_STATUS_UNKNOWN;
107 
108 	if (old_status != charger->bat_status) {
109 		pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name,
110 				old_status,
111 				charger->bat_status);
112 		power_supply_changed(charger->batt_ps);
113 	}
114 
115 	mutex_unlock(&charger->work_lock);
116 }
117 
z2_batt_work(struct work_struct * work)118 static void z2_batt_work(struct work_struct *work)
119 {
120 	struct z2_charger *charger;
121 	charger = container_of(work, struct z2_charger, bat_work);
122 	z2_batt_update(charger);
123 }
124 
z2_charge_switch_irq(int irq,void * devid)125 static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
126 {
127 	struct z2_charger *charger = devid;
128 	schedule_work(&charger->bat_work);
129 	return IRQ_HANDLED;
130 }
131 
z2_batt_ps_init(struct z2_charger * charger,int props)132 static int z2_batt_ps_init(struct z2_charger *charger, int props)
133 {
134 	int i = 0;
135 	enum power_supply_property *prop;
136 	struct z2_battery_info *info = charger->info;
137 
138 	if (info->charge_gpio >= 0)
139 		props++;	/* POWER_SUPPLY_PROP_STATUS */
140 	if (info->batt_tech >= 0)
141 		props++;	/* POWER_SUPPLY_PROP_TECHNOLOGY */
142 	if (info->batt_I2C_reg >= 0)
143 		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_NOW */
144 	if (info->max_voltage >= 0)
145 		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MAX */
146 	if (info->min_voltage >= 0)
147 		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MIN */
148 
149 	prop = kcalloc(props, sizeof(*prop), GFP_KERNEL);
150 	if (!prop)
151 		return -ENOMEM;
152 
153 	prop[i++] = POWER_SUPPLY_PROP_PRESENT;
154 	if (info->charge_gpio >= 0)
155 		prop[i++] = POWER_SUPPLY_PROP_STATUS;
156 	if (info->batt_tech >= 0)
157 		prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
158 	if (info->batt_I2C_reg >= 0)
159 		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
160 	if (info->max_voltage >= 0)
161 		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
162 	if (info->min_voltage >= 0)
163 		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
164 
165 	if (!info->batt_name) {
166 		dev_info(&charger->client->dev,
167 				"Please consider setting proper battery "
168 				"name in platform definition file, falling "
169 				"back to name \" Z2_DEFAULT_NAME \"\n");
170 		charger->batt_ps_desc.name = Z2_DEFAULT_NAME;
171 	} else
172 		charger->batt_ps_desc.name = info->batt_name;
173 
174 	charger->batt_ps_desc.properties	= prop;
175 	charger->batt_ps_desc.num_properties	= props;
176 	charger->batt_ps_desc.type		= POWER_SUPPLY_TYPE_BATTERY;
177 	charger->batt_ps_desc.get_property	= z2_batt_get_property;
178 	charger->batt_ps_desc.external_power_changed =
179 						z2_batt_ext_power_changed;
180 	charger->batt_ps_desc.use_for_apm	= 1;
181 
182 	return 0;
183 }
184 
z2_batt_probe(struct i2c_client * client,const struct i2c_device_id * id)185 static int z2_batt_probe(struct i2c_client *client,
186 				const struct i2c_device_id *id)
187 {
188 	int ret = 0;
189 	int props = 1;	/* POWER_SUPPLY_PROP_PRESENT */
190 	struct z2_charger *charger;
191 	struct z2_battery_info *info = client->dev.platform_data;
192 	struct power_supply_config psy_cfg = {};
193 
194 	if (info == NULL) {
195 		dev_err(&client->dev,
196 			"Please set platform device platform_data"
197 			" to a valid z2_battery_info pointer!\n");
198 		return -EINVAL;
199 	}
200 
201 	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
202 	if (charger == NULL)
203 		return -ENOMEM;
204 
205 	charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
206 	charger->info = info;
207 	charger->client = client;
208 	i2c_set_clientdata(client, charger);
209 	psy_cfg.drv_data = charger;
210 
211 	mutex_init(&charger->work_lock);
212 
213 	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
214 		ret = gpio_request(info->charge_gpio, "BATT CHRG");
215 		if (ret)
216 			goto err;
217 
218 		ret = gpio_direction_input(info->charge_gpio);
219 		if (ret)
220 			goto err2;
221 
222 		irq_set_irq_type(gpio_to_irq(info->charge_gpio),
223 				 IRQ_TYPE_EDGE_BOTH);
224 		ret = request_irq(gpio_to_irq(info->charge_gpio),
225 				z2_charge_switch_irq, 0,
226 				"AC Detect", charger);
227 		if (ret)
228 			goto err3;
229 	}
230 
231 	ret = z2_batt_ps_init(charger, props);
232 	if (ret)
233 		goto err3;
234 
235 	INIT_WORK(&charger->bat_work, z2_batt_work);
236 
237 	charger->batt_ps = power_supply_register(&client->dev,
238 						 &charger->batt_ps_desc,
239 						 &psy_cfg);
240 	if (IS_ERR(charger->batt_ps)) {
241 		ret = PTR_ERR(charger->batt_ps);
242 		goto err4;
243 	}
244 
245 	schedule_work(&charger->bat_work);
246 
247 	return 0;
248 
249 err4:
250 	kfree(charger->batt_ps_desc.properties);
251 err3:
252 	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
253 		free_irq(gpio_to_irq(info->charge_gpio), charger);
254 err2:
255 	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
256 		gpio_free(info->charge_gpio);
257 err:
258 	kfree(charger);
259 	return ret;
260 }
261 
z2_batt_remove(struct i2c_client * client)262 static int z2_batt_remove(struct i2c_client *client)
263 {
264 	struct z2_charger *charger = i2c_get_clientdata(client);
265 	struct z2_battery_info *info = charger->info;
266 
267 	cancel_work_sync(&charger->bat_work);
268 	power_supply_unregister(charger->batt_ps);
269 
270 	kfree(charger->batt_ps_desc.properties);
271 	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
272 		free_irq(gpio_to_irq(info->charge_gpio), charger);
273 		gpio_free(info->charge_gpio);
274 	}
275 
276 	kfree(charger);
277 
278 	return 0;
279 }
280 
281 #ifdef CONFIG_PM
z2_batt_suspend(struct device * dev)282 static int z2_batt_suspend(struct device *dev)
283 {
284 	struct i2c_client *client = to_i2c_client(dev);
285 	struct z2_charger *charger = i2c_get_clientdata(client);
286 
287 	flush_work(&charger->bat_work);
288 	return 0;
289 }
290 
z2_batt_resume(struct device * dev)291 static int z2_batt_resume(struct device *dev)
292 {
293 	struct i2c_client *client = to_i2c_client(dev);
294 	struct z2_charger *charger = i2c_get_clientdata(client);
295 
296 	schedule_work(&charger->bat_work);
297 	return 0;
298 }
299 
300 static const struct dev_pm_ops z2_battery_pm_ops = {
301 	.suspend	= z2_batt_suspend,
302 	.resume		= z2_batt_resume,
303 };
304 
305 #define	Z2_BATTERY_PM_OPS	(&z2_battery_pm_ops)
306 
307 #else
308 #define	Z2_BATTERY_PM_OPS	(NULL)
309 #endif
310 
311 static const struct i2c_device_id z2_batt_id[] = {
312 	{ "aer915", 0 },
313 	{ }
314 };
315 MODULE_DEVICE_TABLE(i2c, z2_batt_id);
316 
317 static struct i2c_driver z2_batt_driver = {
318 	.driver	= {
319 		.name	= "z2-battery",
320 		.pm	= Z2_BATTERY_PM_OPS
321 	},
322 	.probe		= z2_batt_probe,
323 	.remove		= z2_batt_remove,
324 	.id_table	= z2_batt_id,
325 };
326 module_i2c_driver(z2_batt_driver);
327 
328 MODULE_LICENSE("GPL");
329 MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
330 MODULE_DESCRIPTION("Zipit Z2 battery driver");
331