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