1275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes/*
2275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * 1-Wire implementation for the ds2780 chip
3275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes *
4275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * Copyright (C) 2010 Indesign, LLC
5275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes *
6275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * Author: Clifton Barnes <cabarnes@indesign-llc.com>
7275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes *
8275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * Based on w1-ds2760 driver
9275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes *
10275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * This program is free software; you can redistribute it and/or modify
11275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * it under the terms of the GNU General Public License version 2 as
12275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes * published by the Free Software Foundation.
13275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes *
14275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes */
15275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
16275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/kernel.h>
17275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/module.h>
18275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/device.h>
19275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/types.h>
20275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/platform_device.h>
21275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/mutex.h>
22275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include <linux/idr.h>
23275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
24275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include "../w1.h"
25275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include "../w1_int.h"
26275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include "../w1_family.h"
27275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes#include "w1_ds2780.h"
28275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
299fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnesstatic int w1_ds2780_do_io(struct device *dev, char *buf, int addr,
309fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes			size_t count, int io)
31275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
32275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
33275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
349fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	if (addr > DS2780_DATA_SIZE || addr < 0)
359fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes		return 0;
36275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
37275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	count = min_t(int, count, DS2780_DATA_SIZE - addr);
38275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
39275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	if (w1_reset_select_slave(sl) == 0) {
40275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		if (io) {
41275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes			w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
42275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes			w1_write_8(sl->master, addr);
43275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes			w1_write_block(sl->master, buf, count);
44275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		} else {
45275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes			w1_write_8(sl->master, W1_DS2780_READ_DATA);
46275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes			w1_write_8(sl->master, addr);
47275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes			count = w1_read_block(sl->master, buf, count);
48275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		}
49275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	}
50275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
519fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	return count;
529fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes}
539fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes
549fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnesint w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
559fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes			int io)
569fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes{
579fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
589fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	int ret;
599fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes
609fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	if (!dev)
619fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes		return -ENODEV;
629fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes
63b02f8bede217a4b145ecc16d3940c78d83941147NeilBrown	mutex_lock(&sl->master->bus_mutex);
649fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes
659fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	ret = w1_ds2780_do_io(dev, buf, addr, count, io);
669fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes
67b02f8bede217a4b145ecc16d3940c78d83941147NeilBrown	mutex_unlock(&sl->master->bus_mutex);
68275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
699fe678fa2feb4aaac0b4220de63e1b7f8ccebae6Clifton Barnes	return ret;
70275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
71275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton BarnesEXPORT_SYMBOL(w1_ds2780_io);
72275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
73275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesint w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
74275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
75275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
76275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
77275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	if (!dev)
78275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		return -EINVAL;
79275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
80b02f8bede217a4b145ecc16d3940c78d83941147NeilBrown	mutex_lock(&sl->master->bus_mutex);
81275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
82275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	if (w1_reset_select_slave(sl) == 0) {
83275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		w1_write_8(sl->master, cmd);
84275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		w1_write_8(sl->master, addr);
85275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	}
86275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
87b02f8bede217a4b145ecc16d3940c78d83941147NeilBrown	mutex_unlock(&sl->master->bus_mutex);
88275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	return 0;
89275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
90275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton BarnesEXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
91275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
929365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartmanstatic ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
939365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman			     struct bin_attribute *bin_attr, char *buf,
949365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman			     loff_t off, size_t count)
95275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
96275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	struct device *dev = container_of(kobj, struct device, kobj);
97275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	return w1_ds2780_io(dev, buf, off, count, 0);
98275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
99275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
1009365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartmanstatic BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE);
1019365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman
1029365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartmanstatic struct bin_attribute *w1_ds2780_bin_attrs[] = {
1039365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman	&bin_attr_w1_slave,
1049365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman	NULL,
1059365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman};
1069365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman
1079365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartmanstatic const struct attribute_group w1_ds2780_group = {
1089365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman	.bin_attrs = w1_ds2780_bin_attrs,
1099365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman};
1109365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman
1119365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartmanstatic const struct attribute_group *w1_ds2780_groups[] = {
1129365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman	&w1_ds2780_group,
1139365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman	NULL,
114275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes};
115275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
1163e5428177c74df7f3b8c59b2f27f46b82b077e94Jonathan Cameronstatic DEFINE_IDA(bat_ida);
117275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
118275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesstatic int w1_ds2780_add_slave(struct w1_slave *sl)
119275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
120275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	int ret;
121275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	int id;
122275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	struct platform_device *pdev;
123275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
1243e5428177c74df7f3b8c59b2f27f46b82b077e94Jonathan Cameron	id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL);
125275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	if (id < 0) {
126275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		ret = id;
127275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		goto noid;
128275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	}
129275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
130275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	pdev = platform_device_alloc("ds2780-battery", id);
131275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	if (!pdev) {
132275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		ret = -ENOMEM;
133275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		goto pdev_alloc_failed;
134275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	}
135275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	pdev->dev.parent = &sl->dev;
136275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
137275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	ret = platform_device_add(pdev);
138275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	if (ret)
139275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes		goto pdev_add_failed;
140275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
141275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	dev_set_drvdata(&sl->dev, pdev);
142275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
143275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	return 0;
144275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
145275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnespdev_add_failed:
146c5cfedf234fd5ba457c404bf42b6e6aea23b1e69Wei Yongjun	platform_device_put(pdev);
147275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnespdev_alloc_failed:
1483e5428177c74df7f3b8c59b2f27f46b82b077e94Jonathan Cameron	ida_simple_remove(&bat_ida, id);
149275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesnoid:
150275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	return ret;
151275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
152275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
153275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesstatic void w1_ds2780_remove_slave(struct w1_slave *sl)
154275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
155275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	struct platform_device *pdev = dev_get_drvdata(&sl->dev);
156275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	int id = pdev->id;
157275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
158275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	platform_device_unregister(pdev);
1593e5428177c74df7f3b8c59b2f27f46b82b077e94Jonathan Cameron	ida_simple_remove(&bat_ida, id);
160275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
161275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
162275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesstatic struct w1_family_ops w1_ds2780_fops = {
163275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	.add_slave    = w1_ds2780_add_slave,
164275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	.remove_slave = w1_ds2780_remove_slave,
1659365261db45ddf1dc0800ce70830928beb0efd78Greg Kroah-Hartman	.groups       = w1_ds2780_groups,
166275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes};
167275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
168275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesstatic struct w1_family w1_ds2780_family = {
169275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	.fid = W1_FAMILY_DS2780,
170275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	.fops = &w1_ds2780_fops,
171275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes};
172275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
173275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesstatic int __init w1_ds2780_init(void)
174275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
1753e5428177c74df7f3b8c59b2f27f46b82b077e94Jonathan Cameron	ida_init(&bat_ida);
176275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	return w1_register_family(&w1_ds2780_family);
177275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
178275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
179275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesstatic void __exit w1_ds2780_exit(void)
180275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes{
181275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes	w1_unregister_family(&w1_ds2780_family);
1823e5428177c74df7f3b8c59b2f27f46b82b077e94Jonathan Cameron	ida_destroy(&bat_ida);
183275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes}
184275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
185275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesmodule_init(w1_ds2780_init);
186275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnesmodule_exit(w1_ds2780_exit);
187275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton Barnes
188275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton BarnesMODULE_LICENSE("GPL");
189275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton BarnesMODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
190275ac74629c4d8ec430d7edecb16d936f46a47c5Clifton BarnesMODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC");
1918d7bda51888d14c07cbebacc5a10be776477bb63Alexander SteinMODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780));
192