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