2011-05-26 16:26:04 -07:00
/*
* 1 - Wire implementation for the ds2780 chip
*
* Copyright ( C ) 2010 Indesign , LLC
*
* Author : Clifton Barnes < cabarnes @ indesign - llc . com >
*
* Based on w1 - ds2760 driver
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/types.h>
# include <linux/platform_device.h>
# include <linux/mutex.h>
# include <linux/idr.h>
# include "../w1.h"
# include "../w1_int.h"
# include "../w1_family.h"
# include "w1_ds2780.h"
2011-11-02 13:39:52 -07:00
static int w1_ds2780_do_io ( struct device * dev , char * buf , int addr ,
size_t count , int io )
2011-05-26 16:26:04 -07:00
{
struct w1_slave * sl = container_of ( dev , struct w1_slave , dev ) ;
2011-11-02 13:39:52 -07:00
if ( addr > DS2780_DATA_SIZE | | addr < 0 )
return 0 ;
2011-05-26 16:26:04 -07:00
count = min_t ( int , count , DS2780_DATA_SIZE - addr ) ;
if ( w1_reset_select_slave ( sl ) = = 0 ) {
if ( io ) {
w1_write_8 ( sl - > master , W1_DS2780_WRITE_DATA ) ;
w1_write_8 ( sl - > master , addr ) ;
w1_write_block ( sl - > master , buf , count ) ;
} else {
w1_write_8 ( sl - > master , W1_DS2780_READ_DATA ) ;
w1_write_8 ( sl - > master , addr ) ;
count = w1_read_block ( sl - > master , buf , count ) ;
}
}
2011-11-02 13:39:52 -07:00
return count ;
}
int w1_ds2780_io ( struct device * dev , char * buf , int addr , size_t count ,
int io )
{
struct w1_slave * sl = container_of ( dev , struct w1_slave , dev ) ;
int ret ;
if ( ! dev )
return - ENODEV ;
2012-05-18 15:59:52 +10:00
mutex_lock ( & sl - > master - > bus_mutex ) ;
2011-11-02 13:39:52 -07:00
ret = w1_ds2780_do_io ( dev , buf , addr , count , io ) ;
2012-05-18 15:59:52 +10:00
mutex_unlock ( & sl - > master - > bus_mutex ) ;
2011-05-26 16:26:04 -07:00
2011-11-02 13:39:52 -07:00
return ret ;
2011-05-26 16:26:04 -07:00
}
EXPORT_SYMBOL ( w1_ds2780_io ) ;
int w1_ds2780_eeprom_cmd ( struct device * dev , int addr , int cmd )
{
struct w1_slave * sl = container_of ( dev , struct w1_slave , dev ) ;
if ( ! dev )
return - EINVAL ;
2012-05-18 15:59:52 +10:00
mutex_lock ( & sl - > master - > bus_mutex ) ;
2011-05-26 16:26:04 -07:00
if ( w1_reset_select_slave ( sl ) = = 0 ) {
w1_write_8 ( sl - > master , cmd ) ;
w1_write_8 ( sl - > master , addr ) ;
}
2012-05-18 15:59:52 +10:00
mutex_unlock ( & sl - > master - > bus_mutex ) ;
2011-05-26 16:26:04 -07:00
return 0 ;
}
EXPORT_SYMBOL ( w1_ds2780_eeprom_cmd ) ;
static ssize_t w1_ds2780_read_bin ( struct file * filp ,
struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
return w1_ds2780_io ( dev , buf , off , count , 0 ) ;
}
static struct bin_attribute w1_ds2780_bin_attr = {
. attr = {
. name = " w1_slave " ,
. mode = S_IRUGO ,
} ,
. size = DS2780_DATA_SIZE ,
. read = w1_ds2780_read_bin ,
} ;
2011-11-02 13:39:43 -07:00
static DEFINE_IDA ( bat_ida ) ;
2011-05-26 16:26:04 -07:00
static int w1_ds2780_add_slave ( struct w1_slave * sl )
{
int ret ;
int id ;
struct platform_device * pdev ;
2011-11-02 13:39:43 -07:00
id = ida_simple_get ( & bat_ida , 0 , 0 , GFP_KERNEL ) ;
2011-05-26 16:26:04 -07:00
if ( id < 0 ) {
ret = id ;
goto noid ;
}
pdev = platform_device_alloc ( " ds2780-battery " , id ) ;
if ( ! pdev ) {
ret = - ENOMEM ;
goto pdev_alloc_failed ;
}
pdev - > dev . parent = & sl - > dev ;
ret = platform_device_add ( pdev ) ;
if ( ret )
goto pdev_add_failed ;
ret = sysfs_create_bin_file ( & sl - > dev . kobj , & w1_ds2780_bin_attr ) ;
if ( ret )
goto bin_attr_failed ;
dev_set_drvdata ( & sl - > dev , pdev ) ;
return 0 ;
bin_attr_failed :
2013-04-30 15:28:36 -07:00
platform_device_del ( pdev ) ;
2011-05-26 16:26:04 -07:00
pdev_add_failed :
2013-04-30 15:28:36 -07:00
platform_device_put ( pdev ) ;
2011-05-26 16:26:04 -07:00
pdev_alloc_failed :
2011-11-02 13:39:43 -07:00
ida_simple_remove ( & bat_ida , id ) ;
2011-05-26 16:26:04 -07:00
noid :
return ret ;
}
static void w1_ds2780_remove_slave ( struct w1_slave * sl )
{
struct platform_device * pdev = dev_get_drvdata ( & sl - > dev ) ;
int id = pdev - > id ;
platform_device_unregister ( pdev ) ;
2011-11-02 13:39:43 -07:00
ida_simple_remove ( & bat_ida , id ) ;
2011-05-26 16:26:04 -07:00
sysfs_remove_bin_file ( & sl - > dev . kobj , & w1_ds2780_bin_attr ) ;
}
static struct w1_family_ops w1_ds2780_fops = {
. add_slave = w1_ds2780_add_slave ,
. remove_slave = w1_ds2780_remove_slave ,
} ;
static struct w1_family w1_ds2780_family = {
. fid = W1_FAMILY_DS2780 ,
. fops = & w1_ds2780_fops ,
} ;
static int __init w1_ds2780_init ( void )
{
2011-11-02 13:39:43 -07:00
ida_init ( & bat_ida ) ;
2011-05-26 16:26:04 -07:00
return w1_register_family ( & w1_ds2780_family ) ;
}
static void __exit w1_ds2780_exit ( void )
{
w1_unregister_family ( & w1_ds2780_family ) ;
2011-11-02 13:39:43 -07:00
ida_destroy ( & bat_ida ) ;
2011-05-26 16:26:04 -07:00
}
module_init ( w1_ds2780_init ) ;
module_exit ( w1_ds2780_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Clifton Barnes <cabarnes@indesign-llc.com> " ) ;
MODULE_DESCRIPTION ( " 1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC " ) ;
2013-05-26 20:06:50 +02:00
MODULE_ALIAS ( " w1-family- " __stringify ( W1_FAMILY_DS2780 ) ) ;