2005-12-06 13:38:28 +03:00
/**
* ds2482 . c - provides i2c to w1 - master bridge ( s )
* Copyright ( C ) 2005 Ben Gardner < bgardner @ wabtec . com >
*
* The DS2482 is a sensor chip made by Dallas Semiconductor ( Maxim ) .
* It is a I2C to 1 - wire bridge .
* There are two variations : - 100 and - 800 , which have 1 or 8 1 - wire ports .
* The complete datasheet can be obtained from MAXIM ' s website at :
* http : //www.maxim-ic.com/quick_view2.cfm/qv_pk/4382
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/delay.h>
# include <asm/delay.h>
# include "../w1.h"
# include "../w1_int.h"
/**
* The DS2482 registers - there are 3 registers that are addressed by a read
* pointer . The read pointer is set by the last command executed .
*
* To read the data , issue a register read for any address
*/
# define DS2482_CMD_RESET 0xF0 /* No param */
# define DS2482_CMD_SET_READ_PTR 0xE1 /* Param: DS2482_PTR_CODE_xxx */
# define DS2482_CMD_CHANNEL_SELECT 0xC3 /* Param: Channel byte - DS2482-800 only */
# define DS2482_CMD_WRITE_CONFIG 0xD2 /* Param: Config byte */
# define DS2482_CMD_1WIRE_RESET 0xB4 /* Param: None */
# define DS2482_CMD_1WIRE_SINGLE_BIT 0x87 /* Param: Bit byte (bit7) */
# define DS2482_CMD_1WIRE_WRITE_BYTE 0xA5 /* Param: Data byte */
# define DS2482_CMD_1WIRE_READ_BYTE 0x96 /* Param: None */
/* Note to read the byte, Set the ReadPtr to Data then read (any addr) */
# define DS2482_CMD_1WIRE_TRIPLET 0x78 /* Param: Dir byte (bit7) */
/* Values for DS2482_CMD_SET_READ_PTR */
# define DS2482_PTR_CODE_STATUS 0xF0
# define DS2482_PTR_CODE_DATA 0xE1
# define DS2482_PTR_CODE_CHANNEL 0xD2 /* DS2482-800 only */
# define DS2482_PTR_CODE_CONFIG 0xC3
/**
* Configure Register bit definitions
* The top 4 bits always read 0.
* To write , the top nibble must be the 1 ' s compl . of the low nibble .
*/
# define DS2482_REG_CFG_1WS 0x08
# define DS2482_REG_CFG_SPU 0x04
# define DS2482_REG_CFG_PPM 0x02
# define DS2482_REG_CFG_APU 0x01
/**
* Write and verify codes for the CHANNEL_SELECT command ( DS2482 - 800 only ) .
* To set the channel , write the value at the index of the channel .
* Read and compare against the corresponding value to verify the change .
*/
static const u8 ds2482_chan_wr [ 8 ] =
{ 0xF0 , 0xE1 , 0xD2 , 0xC3 , 0xB4 , 0xA5 , 0x96 , 0x87 } ;
static const u8 ds2482_chan_rd [ 8 ] =
{ 0xB8 , 0xB1 , 0xAA , 0xA3 , 0x9C , 0x95 , 0x8E , 0x87 } ;
/**
* Status Register bit definitions ( read only )
*/
# define DS2482_REG_STS_DIR 0x80
# define DS2482_REG_STS_TSB 0x40
# define DS2482_REG_STS_SBR 0x20
# define DS2482_REG_STS_RST 0x10
# define DS2482_REG_STS_LL 0x08
# define DS2482_REG_STS_SD 0x04
# define DS2482_REG_STS_PPD 0x02
# define DS2482_REG_STS_1WB 0x01
2008-07-16 21:30:07 +04:00
static int ds2482_probe ( struct i2c_client * client ,
const struct i2c_device_id * id ) ;
static int ds2482_remove ( struct i2c_client * client ) ;
2005-12-06 13:38:28 +03:00
/**
* Driver data ( common to all clients )
*/
2008-07-16 21:30:07 +04:00
static const struct i2c_device_id ds2482_id [ ] = {
{ " ds2482 " , 0 } ,
{ }
} ;
2005-12-06 13:38:28 +03:00
static struct i2c_driver ds2482_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " ds2482 " ,
} ,
2008-07-16 21:30:07 +04:00
. probe = ds2482_probe ,
. remove = ds2482_remove ,
. id_table = ds2482_id ,
2005-12-06 13:38:28 +03:00
} ;
/*
* Client data ( each client gets its own )
*/
struct ds2482_data ;
struct ds2482_w1_chan {
struct ds2482_data * pdev ;
u8 channel ;
struct w1_bus_master w1_bm ;
} ;
struct ds2482_data {
2008-07-16 21:30:07 +04:00
struct i2c_client * client ;
2006-04-03 12:04:27 +04:00
struct mutex access_lock ;
2005-12-06 13:38:28 +03:00
/* 1-wire interface(s) */
int w1_count ; /* 1 or 8 */
struct ds2482_w1_chan w1_ch [ 8 ] ;
/* per-device values */
u8 channel ;
u8 read_prt ; /* see DS2482_PTR_CODE_xxx */
u8 reg_config ;
} ;
/**
* Sets the read pointer .
* @ param pdev The ds2482 client pointer
* @ param read_ptr see DS2482_PTR_CODE_xxx above
* @ return - 1 on failure , 0 on success
*/
static inline int ds2482_select_register ( struct ds2482_data * pdev , u8 read_ptr )
{
if ( pdev - > read_prt ! = read_ptr ) {
2008-07-16 21:30:07 +04:00
if ( i2c_smbus_write_byte_data ( pdev - > client ,
2005-12-06 13:38:28 +03:00
DS2482_CMD_SET_READ_PTR ,
read_ptr ) < 0 )
return - 1 ;
pdev - > read_prt = read_ptr ;
}
return 0 ;
}
/**
* Sends a command without a parameter
* @ param pdev The ds2482 client pointer
* @ param cmd DS2482_CMD_RESET ,
* DS2482_CMD_1WIRE_RESET ,
* DS2482_CMD_1WIRE_READ_BYTE
* @ return - 1 on failure , 0 on success
*/
static inline int ds2482_send_cmd ( struct ds2482_data * pdev , u8 cmd )
{
2008-07-16 21:30:07 +04:00
if ( i2c_smbus_write_byte ( pdev - > client , cmd ) < 0 )
2005-12-06 13:38:28 +03:00
return - 1 ;
pdev - > read_prt = DS2482_PTR_CODE_STATUS ;
return 0 ;
}
/**
* Sends a command with a parameter
* @ param pdev The ds2482 client pointer
* @ param cmd DS2482_CMD_WRITE_CONFIG ,
* DS2482_CMD_1WIRE_SINGLE_BIT ,
* DS2482_CMD_1WIRE_WRITE_BYTE ,
* DS2482_CMD_1WIRE_TRIPLET
* @ param byte The data to send
* @ return - 1 on failure , 0 on success
*/
static inline int ds2482_send_cmd_data ( struct ds2482_data * pdev ,
u8 cmd , u8 byte )
{
2008-07-16 21:30:07 +04:00
if ( i2c_smbus_write_byte_data ( pdev - > client , cmd , byte ) < 0 )
2005-12-06 13:38:28 +03:00
return - 1 ;
/* all cmds leave in STATUS, except CONFIG */
pdev - > read_prt = ( cmd ! = DS2482_CMD_WRITE_CONFIG ) ?
DS2482_PTR_CODE_STATUS : DS2482_PTR_CODE_CONFIG ;
return 0 ;
}
/*
* 1 - Wire interface code
*/
# define DS2482_WAIT_IDLE_TIMEOUT 100
/**
* Waits until the 1 - wire interface is idle ( not busy )
*
* @ param pdev Pointer to the device structure
* @ return the last value read from status or - 1 ( failure )
*/
static int ds2482_wait_1wire_idle ( struct ds2482_data * pdev )
{
int temp = - 1 ;
int retries = 0 ;
if ( ! ds2482_select_register ( pdev , DS2482_PTR_CODE_STATUS ) ) {
do {
2008-07-16 21:30:07 +04:00
temp = i2c_smbus_read_byte ( pdev - > client ) ;
2005-12-06 13:38:28 +03:00
} while ( ( temp > = 0 ) & & ( temp & DS2482_REG_STS_1WB ) & &
2006-06-29 22:33:22 +04:00
( + + retries < DS2482_WAIT_IDLE_TIMEOUT ) ) ;
2005-12-06 13:38:28 +03:00
}
2010-03-11 02:23:49 +03:00
if ( retries > = DS2482_WAIT_IDLE_TIMEOUT )
2005-12-06 13:38:28 +03:00
printk ( KERN_ERR " %s: timeout on channel %d \n " ,
__func__ , pdev - > channel ) ;
return temp ;
}
/**
* Selects a w1 channel .
* The 1 - wire interface must be idle before calling this function .
*
* @ param pdev The ds2482 client pointer
* @ param channel 0 - 7
* @ return - 1 ( failure ) or 0 ( success )
*/
static int ds2482_set_channel ( struct ds2482_data * pdev , u8 channel )
{
2008-07-16 21:30:07 +04:00
if ( i2c_smbus_write_byte_data ( pdev - > client , DS2482_CMD_CHANNEL_SELECT ,
2005-12-06 13:38:28 +03:00
ds2482_chan_wr [ channel ] ) < 0 )
return - 1 ;
pdev - > read_prt = DS2482_PTR_CODE_CHANNEL ;
pdev - > channel = - 1 ;
2008-07-16 21:30:07 +04:00
if ( i2c_smbus_read_byte ( pdev - > client ) = = ds2482_chan_rd [ channel ] ) {
2005-12-06 13:38:28 +03:00
pdev - > channel = channel ;
return 0 ;
}
return - 1 ;
}
/**
* Performs the touch - bit function , which writes a 0 or 1 and reads the level .
*
* @ param data The ds2482 channel pointer
* @ param bit The level to write : 0 or non - zero
* @ return The level read : 0 or 1
*/
static u8 ds2482_w1_touch_bit ( void * data , u8 bit )
{
struct ds2482_w1_chan * pchan = data ;
struct ds2482_data * pdev = pchan - > pdev ;
int status = - 1 ;
2006-04-03 12:04:27 +04:00
mutex_lock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Select the channel */
ds2482_wait_1wire_idle ( pdev ) ;
if ( pdev - > w1_count > 1 )
ds2482_set_channel ( pdev , pchan - > channel ) ;
/* Send the touch command, wait until 1WB == 0, return the status */
if ( ! ds2482_send_cmd_data ( pdev , DS2482_CMD_1WIRE_SINGLE_BIT ,
bit ? 0xFF : 0 ) )
status = ds2482_wait_1wire_idle ( pdev ) ;
2006-04-03 12:04:27 +04:00
mutex_unlock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
return ( status & DS2482_REG_STS_SBR ) ? 1 : 0 ;
}
/**
* Performs the triplet function , which reads two bits and writes a bit .
* The bit written is determined by the two reads :
* 00 = > dbit , 01 = > 0 , 10 = > 1
*
* @ param data The ds2482 channel pointer
* @ param dbit The direction to choose if both branches are valid
* @ return b0 = read1 b1 = read2 b3 = bit written
*/
static u8 ds2482_w1_triplet ( void * data , u8 dbit )
{
struct ds2482_w1_chan * pchan = data ;
struct ds2482_data * pdev = pchan - > pdev ;
int status = ( 3 < < 5 ) ;
2006-04-03 12:04:27 +04:00
mutex_lock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Select the channel */
ds2482_wait_1wire_idle ( pdev ) ;
if ( pdev - > w1_count > 1 )
ds2482_set_channel ( pdev , pchan - > channel ) ;
/* Send the triplet command, wait until 1WB == 0, return the status */
if ( ! ds2482_send_cmd_data ( pdev , DS2482_CMD_1WIRE_TRIPLET ,
dbit ? 0xFF : 0 ) )
status = ds2482_wait_1wire_idle ( pdev ) ;
2006-04-03 12:04:27 +04:00
mutex_unlock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Decode the status */
return ( status > > 5 ) ;
}
/**
* Performs the write byte function .
*
* @ param data The ds2482 channel pointer
* @ param byte The value to write
*/
static void ds2482_w1_write_byte ( void * data , u8 byte )
{
struct ds2482_w1_chan * pchan = data ;
struct ds2482_data * pdev = pchan - > pdev ;
2006-04-03 12:04:27 +04:00
mutex_lock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Select the channel */
ds2482_wait_1wire_idle ( pdev ) ;
if ( pdev - > w1_count > 1 )
ds2482_set_channel ( pdev , pchan - > channel ) ;
/* Send the write byte command */
ds2482_send_cmd_data ( pdev , DS2482_CMD_1WIRE_WRITE_BYTE , byte ) ;
2006-04-03 12:04:27 +04:00
mutex_unlock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
}
/**
* Performs the read byte function .
*
* @ param data The ds2482 channel pointer
* @ return The value read
*/
static u8 ds2482_w1_read_byte ( void * data )
{
struct ds2482_w1_chan * pchan = data ;
struct ds2482_data * pdev = pchan - > pdev ;
int result ;
2006-04-03 12:04:27 +04:00
mutex_lock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Select the channel */
ds2482_wait_1wire_idle ( pdev ) ;
if ( pdev - > w1_count > 1 )
ds2482_set_channel ( pdev , pchan - > channel ) ;
/* Send the read byte command */
ds2482_send_cmd ( pdev , DS2482_CMD_1WIRE_READ_BYTE ) ;
/* Wait until 1WB == 0 */
ds2482_wait_1wire_idle ( pdev ) ;
/* Select the data register */
ds2482_select_register ( pdev , DS2482_PTR_CODE_DATA ) ;
/* Read the data byte */
2008-07-16 21:30:07 +04:00
result = i2c_smbus_read_byte ( pdev - > client ) ;
2005-12-06 13:38:28 +03:00
2006-04-03 12:04:27 +04:00
mutex_unlock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
return result ;
}
/**
* Sends a reset on the 1 - wire interface
*
* @ param data The ds2482 channel pointer
* @ return 0 = Device present , 1 = No device present or error
*/
static u8 ds2482_w1_reset_bus ( void * data )
{
struct ds2482_w1_chan * pchan = data ;
struct ds2482_data * pdev = pchan - > pdev ;
int err ;
u8 retval = 1 ;
2006-04-03 12:04:27 +04:00
mutex_lock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Select the channel */
ds2482_wait_1wire_idle ( pdev ) ;
if ( pdev - > w1_count > 1 )
ds2482_set_channel ( pdev , pchan - > channel ) ;
/* Send the reset command */
err = ds2482_send_cmd ( pdev , DS2482_CMD_1WIRE_RESET ) ;
if ( err > = 0 ) {
/* Wait until the reset is complete */
err = ds2482_wait_1wire_idle ( pdev ) ;
retval = ! ( err & DS2482_REG_STS_PPD ) ;
/* If the chip did reset since detect, re-config it */
if ( err & DS2482_REG_STS_RST )
ds2482_send_cmd_data ( pdev , DS2482_CMD_WRITE_CONFIG ,
0xF0 ) ;
}
2006-04-03 12:04:27 +04:00
mutex_unlock ( & pdev - > access_lock ) ;
2005-12-06 13:38:28 +03:00
return retval ;
}
2008-07-16 21:30:07 +04:00
static int ds2482_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-12-06 13:38:28 +03:00
{
struct ds2482_data * data ;
2008-07-16 21:30:07 +04:00
int err = - ENODEV ;
2005-12-06 13:38:28 +03:00
int temp1 ;
int idx ;
2009-10-05 00:53:41 +04:00
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
I2C_FUNC_SMBUS_BYTE ) )
return - ENODEV ;
2005-12-06 13:38:28 +03:00
if ( ! ( data = kzalloc ( sizeof ( struct ds2482_data ) , GFP_KERNEL ) ) ) {
err = - ENOMEM ;
goto exit ;
}
2008-07-16 21:30:07 +04:00
data - > client = client ;
i2c_set_clientdata ( client , data ) ;
2005-12-06 13:38:28 +03:00
/* Reset the device (sets the read_ptr to status) */
if ( ds2482_send_cmd ( data , DS2482_CMD_RESET ) < 0 ) {
2008-07-16 21:30:07 +04:00
dev_warn ( & client - > dev , " DS2482 reset failed. \n " ) ;
2005-12-06 13:38:28 +03:00
goto exit_free ;
}
/* Sleep at least 525ns to allow the reset to complete */
ndelay ( 525 ) ;
/* Read the status byte - only reset bit and line should be set */
2008-07-16 21:30:07 +04:00
temp1 = i2c_smbus_read_byte ( client ) ;
2005-12-06 13:38:28 +03:00
if ( temp1 ! = ( DS2482_REG_STS_LL | DS2482_REG_STS_RST ) ) {
2008-07-16 21:30:07 +04:00
dev_warn ( & client - > dev , " DS2482 reset status "
" 0x%02X - not a DS2482 \n " , temp1 ) ;
2005-12-06 13:38:28 +03:00
goto exit_free ;
}
/* Detect the 8-port version */
data - > w1_count = 1 ;
if ( ds2482_set_channel ( data , 7 ) = = 0 )
data - > w1_count = 8 ;
/* Set all config items to 0 (off) */
ds2482_send_cmd_data ( data , DS2482_CMD_WRITE_CONFIG , 0xF0 ) ;
2006-04-03 12:04:27 +04:00
mutex_init ( & data - > access_lock ) ;
2005-12-06 13:38:28 +03:00
/* Register 1-wire interface(s) */
for ( idx = 0 ; idx < data - > w1_count ; idx + + ) {
data - > w1_ch [ idx ] . pdev = data ;
data - > w1_ch [ idx ] . channel = idx ;
/* Populate all the w1 bus master stuff */
data - > w1_ch [ idx ] . w1_bm . data = & data - > w1_ch [ idx ] ;
data - > w1_ch [ idx ] . w1_bm . read_byte = ds2482_w1_read_byte ;
data - > w1_ch [ idx ] . w1_bm . write_byte = ds2482_w1_write_byte ;
data - > w1_ch [ idx ] . w1_bm . touch_bit = ds2482_w1_touch_bit ;
data - > w1_ch [ idx ] . w1_bm . triplet = ds2482_w1_triplet ;
data - > w1_ch [ idx ] . w1_bm . reset_bus = ds2482_w1_reset_bus ;
err = w1_add_master_device ( & data - > w1_ch [ idx ] . w1_bm ) ;
if ( err ) {
data - > w1_ch [ idx ] . pdev = NULL ;
goto exit_w1_remove ;
}
}
return 0 ;
exit_w1_remove :
for ( idx = 0 ; idx < data - > w1_count ; idx + + ) {
if ( data - > w1_ch [ idx ] . pdev ! = NULL )
w1_remove_master_device ( & data - > w1_ch [ idx ] . w1_bm ) ;
}
exit_free :
kfree ( data ) ;
exit :
return err ;
}
2008-07-16 21:30:07 +04:00
static int ds2482_remove ( struct i2c_client * client )
2005-12-06 13:38:28 +03:00
{
struct ds2482_data * data = i2c_get_clientdata ( client ) ;
2008-07-16 21:30:07 +04:00
int idx ;
2005-12-06 13:38:28 +03:00
/* Unregister the 1-wire bridge(s) */
for ( idx = 0 ; idx < data - > w1_count ; idx + + ) {
if ( data - > w1_ch [ idx ] . pdev ! = NULL )
w1_remove_master_device ( & data - > w1_ch [ idx ] . w1_bm ) ;
}
/* Free the memory */
kfree ( data ) ;
return 0 ;
}
static int __init sensors_ds2482_init ( void )
{
return i2c_add_driver ( & ds2482_driver ) ;
}
static void __exit sensors_ds2482_exit ( void )
{
i2c_del_driver ( & ds2482_driver ) ;
}
MODULE_AUTHOR ( " Ben Gardner <bgardner@wabtec.com> " ) ;
MODULE_DESCRIPTION ( " DS2482 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( sensors_ds2482_init ) ;
module_exit ( sensors_ds2482_exit ) ;