xref: /openbmc/linux/drivers/w1/slaves/w1_ds2780.c (revision 275ac746)
1275ac746SClifton Barnes /*
2275ac746SClifton Barnes  * 1-Wire implementation for the ds2780 chip
3275ac746SClifton Barnes  *
4275ac746SClifton Barnes  * Copyright (C) 2010 Indesign, LLC
5275ac746SClifton Barnes  *
6275ac746SClifton Barnes  * Author: Clifton Barnes <cabarnes@indesign-llc.com>
7275ac746SClifton Barnes  *
8275ac746SClifton Barnes  * Based on w1-ds2760 driver
9275ac746SClifton Barnes  *
10275ac746SClifton Barnes  * This program is free software; you can redistribute it and/or modify
11275ac746SClifton Barnes  * it under the terms of the GNU General Public License version 2 as
12275ac746SClifton Barnes  * published by the Free Software Foundation.
13275ac746SClifton Barnes  *
14275ac746SClifton Barnes  */
15275ac746SClifton Barnes 
16275ac746SClifton Barnes #include <linux/kernel.h>
17275ac746SClifton Barnes #include <linux/module.h>
18275ac746SClifton Barnes #include <linux/device.h>
19275ac746SClifton Barnes #include <linux/types.h>
20275ac746SClifton Barnes #include <linux/platform_device.h>
21275ac746SClifton Barnes #include <linux/mutex.h>
22275ac746SClifton Barnes #include <linux/idr.h>
23275ac746SClifton Barnes 
24275ac746SClifton Barnes #include "../w1.h"
25275ac746SClifton Barnes #include "../w1_int.h"
26275ac746SClifton Barnes #include "../w1_family.h"
27275ac746SClifton Barnes #include "w1_ds2780.h"
28275ac746SClifton Barnes 
29275ac746SClifton Barnes int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
30275ac746SClifton Barnes 			int io)
31275ac746SClifton Barnes {
32275ac746SClifton Barnes 	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
33275ac746SClifton Barnes 
34275ac746SClifton Barnes 	if (!dev)
35275ac746SClifton Barnes 		return -ENODEV;
36275ac746SClifton Barnes 
37275ac746SClifton Barnes 	mutex_lock(&sl->master->mutex);
38275ac746SClifton Barnes 
39275ac746SClifton Barnes 	if (addr > DS2780_DATA_SIZE || addr < 0) {
40275ac746SClifton Barnes 		count = 0;
41275ac746SClifton Barnes 		goto out;
42275ac746SClifton Barnes 	}
43275ac746SClifton Barnes 	count = min_t(int, count, DS2780_DATA_SIZE - addr);
44275ac746SClifton Barnes 
45275ac746SClifton Barnes 	if (w1_reset_select_slave(sl) == 0) {
46275ac746SClifton Barnes 		if (io) {
47275ac746SClifton Barnes 			w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
48275ac746SClifton Barnes 			w1_write_8(sl->master, addr);
49275ac746SClifton Barnes 			w1_write_block(sl->master, buf, count);
50275ac746SClifton Barnes 			/* XXX w1_write_block returns void, not n_written */
51275ac746SClifton Barnes 		} else {
52275ac746SClifton Barnes 			w1_write_8(sl->master, W1_DS2780_READ_DATA);
53275ac746SClifton Barnes 			w1_write_8(sl->master, addr);
54275ac746SClifton Barnes 			count = w1_read_block(sl->master, buf, count);
55275ac746SClifton Barnes 		}
56275ac746SClifton Barnes 	}
57275ac746SClifton Barnes 
58275ac746SClifton Barnes out:
59275ac746SClifton Barnes 	mutex_unlock(&sl->master->mutex);
60275ac746SClifton Barnes 
61275ac746SClifton Barnes 	return count;
62275ac746SClifton Barnes }
63275ac746SClifton Barnes EXPORT_SYMBOL(w1_ds2780_io);
64275ac746SClifton Barnes 
65275ac746SClifton Barnes int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
66275ac746SClifton Barnes {
67275ac746SClifton Barnes 	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
68275ac746SClifton Barnes 
69275ac746SClifton Barnes 	if (!dev)
70275ac746SClifton Barnes 		return -EINVAL;
71275ac746SClifton Barnes 
72275ac746SClifton Barnes 	mutex_lock(&sl->master->mutex);
73275ac746SClifton Barnes 
74275ac746SClifton Barnes 	if (w1_reset_select_slave(sl) == 0) {
75275ac746SClifton Barnes 		w1_write_8(sl->master, cmd);
76275ac746SClifton Barnes 		w1_write_8(sl->master, addr);
77275ac746SClifton Barnes 	}
78275ac746SClifton Barnes 
79275ac746SClifton Barnes 	mutex_unlock(&sl->master->mutex);
80275ac746SClifton Barnes 	return 0;
81275ac746SClifton Barnes }
82275ac746SClifton Barnes EXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
83275ac746SClifton Barnes 
84275ac746SClifton Barnes static ssize_t w1_ds2780_read_bin(struct file *filp,
85275ac746SClifton Barnes 				  struct kobject *kobj,
86275ac746SClifton Barnes 				  struct bin_attribute *bin_attr,
87275ac746SClifton Barnes 				  char *buf, loff_t off, size_t count)
88275ac746SClifton Barnes {
89275ac746SClifton Barnes 	struct device *dev = container_of(kobj, struct device, kobj);
90275ac746SClifton Barnes 	return w1_ds2780_io(dev, buf, off, count, 0);
91275ac746SClifton Barnes }
92275ac746SClifton Barnes 
93275ac746SClifton Barnes static struct bin_attribute w1_ds2780_bin_attr = {
94275ac746SClifton Barnes 	.attr = {
95275ac746SClifton Barnes 		.name = "w1_slave",
96275ac746SClifton Barnes 		.mode = S_IRUGO,
97275ac746SClifton Barnes 	},
98275ac746SClifton Barnes 	.size = DS2780_DATA_SIZE,
99275ac746SClifton Barnes 	.read = w1_ds2780_read_bin,
100275ac746SClifton Barnes };
101275ac746SClifton Barnes 
102275ac746SClifton Barnes static DEFINE_IDR(bat_idr);
103275ac746SClifton Barnes static DEFINE_MUTEX(bat_idr_lock);
104275ac746SClifton Barnes 
105275ac746SClifton Barnes static int new_bat_id(void)
106275ac746SClifton Barnes {
107275ac746SClifton Barnes 	int ret;
108275ac746SClifton Barnes 
109275ac746SClifton Barnes 	while (1) {
110275ac746SClifton Barnes 		int id;
111275ac746SClifton Barnes 
112275ac746SClifton Barnes 		ret = idr_pre_get(&bat_idr, GFP_KERNEL);
113275ac746SClifton Barnes 		if (ret == 0)
114275ac746SClifton Barnes 			return -ENOMEM;
115275ac746SClifton Barnes 
116275ac746SClifton Barnes 		mutex_lock(&bat_idr_lock);
117275ac746SClifton Barnes 		ret = idr_get_new(&bat_idr, NULL, &id);
118275ac746SClifton Barnes 		mutex_unlock(&bat_idr_lock);
119275ac746SClifton Barnes 
120275ac746SClifton Barnes 		if (ret == 0) {
121275ac746SClifton Barnes 			ret = id & MAX_ID_MASK;
122275ac746SClifton Barnes 			break;
123275ac746SClifton Barnes 		} else if (ret == -EAGAIN) {
124275ac746SClifton Barnes 			continue;
125275ac746SClifton Barnes 		} else {
126275ac746SClifton Barnes 			break;
127275ac746SClifton Barnes 		}
128275ac746SClifton Barnes 	}
129275ac746SClifton Barnes 
130275ac746SClifton Barnes 	return ret;
131275ac746SClifton Barnes }
132275ac746SClifton Barnes 
133275ac746SClifton Barnes static void release_bat_id(int id)
134275ac746SClifton Barnes {
135275ac746SClifton Barnes 	mutex_lock(&bat_idr_lock);
136275ac746SClifton Barnes 	idr_remove(&bat_idr, id);
137275ac746SClifton Barnes 	mutex_unlock(&bat_idr_lock);
138275ac746SClifton Barnes }
139275ac746SClifton Barnes 
140275ac746SClifton Barnes static int w1_ds2780_add_slave(struct w1_slave *sl)
141275ac746SClifton Barnes {
142275ac746SClifton Barnes 	int ret;
143275ac746SClifton Barnes 	int id;
144275ac746SClifton Barnes 	struct platform_device *pdev;
145275ac746SClifton Barnes 
146275ac746SClifton Barnes 	id = new_bat_id();
147275ac746SClifton Barnes 	if (id < 0) {
148275ac746SClifton Barnes 		ret = id;
149275ac746SClifton Barnes 		goto noid;
150275ac746SClifton Barnes 	}
151275ac746SClifton Barnes 
152275ac746SClifton Barnes 	pdev = platform_device_alloc("ds2780-battery", id);
153275ac746SClifton Barnes 	if (!pdev) {
154275ac746SClifton Barnes 		ret = -ENOMEM;
155275ac746SClifton Barnes 		goto pdev_alloc_failed;
156275ac746SClifton Barnes 	}
157275ac746SClifton Barnes 	pdev->dev.parent = &sl->dev;
158275ac746SClifton Barnes 
159275ac746SClifton Barnes 	ret = platform_device_add(pdev);
160275ac746SClifton Barnes 	if (ret)
161275ac746SClifton Barnes 		goto pdev_add_failed;
162275ac746SClifton Barnes 
163275ac746SClifton Barnes 	ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
164275ac746SClifton Barnes 	if (ret)
165275ac746SClifton Barnes 		goto bin_attr_failed;
166275ac746SClifton Barnes 
167275ac746SClifton Barnes 	dev_set_drvdata(&sl->dev, pdev);
168275ac746SClifton Barnes 
169275ac746SClifton Barnes 	return 0;
170275ac746SClifton Barnes 
171275ac746SClifton Barnes bin_attr_failed:
172275ac746SClifton Barnes pdev_add_failed:
173275ac746SClifton Barnes 	platform_device_unregister(pdev);
174275ac746SClifton Barnes pdev_alloc_failed:
175275ac746SClifton Barnes 	release_bat_id(id);
176275ac746SClifton Barnes noid:
177275ac746SClifton Barnes 	return ret;
178275ac746SClifton Barnes }
179275ac746SClifton Barnes 
180275ac746SClifton Barnes static void w1_ds2780_remove_slave(struct w1_slave *sl)
181275ac746SClifton Barnes {
182275ac746SClifton Barnes 	struct platform_device *pdev = dev_get_drvdata(&sl->dev);
183275ac746SClifton Barnes 	int id = pdev->id;
184275ac746SClifton Barnes 
185275ac746SClifton Barnes 	platform_device_unregister(pdev);
186275ac746SClifton Barnes 	release_bat_id(id);
187275ac746SClifton Barnes 	sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
188275ac746SClifton Barnes }
189275ac746SClifton Barnes 
190275ac746SClifton Barnes static struct w1_family_ops w1_ds2780_fops = {
191275ac746SClifton Barnes 	.add_slave    = w1_ds2780_add_slave,
192275ac746SClifton Barnes 	.remove_slave = w1_ds2780_remove_slave,
193275ac746SClifton Barnes };
194275ac746SClifton Barnes 
195275ac746SClifton Barnes static struct w1_family w1_ds2780_family = {
196275ac746SClifton Barnes 	.fid = W1_FAMILY_DS2780,
197275ac746SClifton Barnes 	.fops = &w1_ds2780_fops,
198275ac746SClifton Barnes };
199275ac746SClifton Barnes 
200275ac746SClifton Barnes static int __init w1_ds2780_init(void)
201275ac746SClifton Barnes {
202275ac746SClifton Barnes 	idr_init(&bat_idr);
203275ac746SClifton Barnes 	return w1_register_family(&w1_ds2780_family);
204275ac746SClifton Barnes }
205275ac746SClifton Barnes 
206275ac746SClifton Barnes static void __exit w1_ds2780_exit(void)
207275ac746SClifton Barnes {
208275ac746SClifton Barnes 	w1_unregister_family(&w1_ds2780_family);
209275ac746SClifton Barnes 	idr_destroy(&bat_idr);
210275ac746SClifton Barnes }
211275ac746SClifton Barnes 
212275ac746SClifton Barnes module_init(w1_ds2780_init);
213275ac746SClifton Barnes module_exit(w1_ds2780_exit);
214275ac746SClifton Barnes 
215275ac746SClifton Barnes MODULE_LICENSE("GPL");
216275ac746SClifton Barnes MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
217275ac746SClifton Barnes MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC");
218