2019-05-24 13:04:07 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
2005-05-20 22:33:25 +04:00
* w1_therm . c
2005-04-17 02:20:36 +04:00
*
2011-08-26 02:59:06 +04:00
* Copyright ( c ) 2004 Evgeniy Polyakov < zbr @ ioremap . net >
2005-04-17 02:20:36 +04:00
*/
# include <asm/types.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2006-10-18 09:47:25 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/device.h>
# include <linux/types.h>
2014-01-16 08:29:24 +04:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
2017-08-31 02:34:35 +03:00
# include <linux/hwmon.h>
2020-05-11 23:36:10 +03:00
# include <linux/string.h>
2020-09-04 19:00:03 +03:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
2017-06-05 16:52:08 +03:00
# include <linux/w1.h>
# define W1_THERM_DS18S20 0x10
# define W1_THERM_DS1822 0x22
# define W1_THERM_DS18B20 0x28
# define W1_THERM_DS1825 0x3B
# define W1_THERM_DS28EA00 0x42
2005-04-17 02:20:36 +04:00
2020-05-11 23:35:35 +03:00
/*
* Allow the strong pullup to be disabled , but default to enabled .
2008-10-16 09:04:43 +04:00
* If it was disabled a parasite powered device might not get the require
* current to do a temperature conversion . If it is enabled parasite powered
* devices have a better chance of getting the current required .
2013-02-17 23:51:20 +04:00
* In case the parasite power - detection is not working ( seems to be the case
* for some DS18S20 ) the strong pullup can also be forced , regardless of the
* power state of the devices .
*
* Summary of options :
* - strong_pullup = 0 Disable strong pullup completely
* - strong_pullup = 1 Enable automatic strong pullup detection
* - strong_pullup = 2 Force strong pullup
2008-10-16 09:04:43 +04:00
*/
static int w1_strong_pullup = 1 ;
module_param_named ( strong_pullup , w1_strong_pullup , int , 0 ) ;
2020-05-11 23:38:20 +03:00
/* Counter for devices supporting bulk reading */
static u16 bulk_read_device_counter ; /* =0 as per C standard */
2020-05-11 23:37:25 +03:00
/* This command should be in public header w1.h but is not */
# define W1_RECALL_EEPROM 0xB8
2020-05-11 23:36:50 +03:00
/* Nb of try for an operation */
# define W1_THERM_MAX_TRY 5
/* ms delay to retry bus mutex */
# define W1_THERM_RETRY_DELAY 20
2020-05-11 23:37:25 +03:00
/* delay in ms to write in EEPROM */
# define W1_THERM_EEPROM_WRITE_DELAY 10
# define EEPROM_CMD_WRITE "save" /* cmd for write eeprom sysfs */
# define EEPROM_CMD_READ "restore" /* cmd for read eeprom sysfs */
2020-05-11 23:38:20 +03:00
# define BULK_TRIGGER_CMD "trigger" /* cmd to trigger a bulk read */
2020-05-11 23:37:25 +03:00
2021-03-19 08:25:54 +03:00
# define MIN_TEMP -55 /* min temperature that can be measured */
# define MAX_TEMP 125 /* max temperature that can be measured */
2020-05-11 23:38:01 +03:00
2020-09-04 19:00:03 +03:00
/* Allowed values for sysfs conv_time attribute */
# define CONV_TIME_DEFAULT 0
# define CONV_TIME_MEASURE 1
/* Bits in sysfs "features" value */
# define W1_THERM_CHECK_RESULT 1 /* Enable conversion success check */
# define W1_THERM_POLL_COMPLETION 2 /* Poll for conversion completion */
# define W1_THERM_FEATURES_MASK 3 /* All values mask */
/* Poll period in milliseconds. Should be less then a shortest operation on the device */
# define W1_POLL_PERIOD 32
# define W1_POLL_CONVERT_TEMP 2000 /* Timeout for W1_CONVERT_TEMP, ms */
# define W1_POLL_RECALL_EEPROM 500 /* Timeout for W1_RECALL_EEPROM, ms*/
2020-09-04 19:00:04 +03:00
/* Masks for resolution functions, work with all devices */
/* Bit mask for config register for all devices, bits 7,6,5 */
# define W1_THERM_RESOLUTION_MASK 0xE0
/* Bit offset of resolution in config register for all devices */
# define W1_THERM_RESOLUTION_SHIFT 5
/* Bit offset of resolution in config register for all devices */
# define W1_THERM_RESOLUTION_SHIFT 5
/* Add this to bit value to get resolution */
# define W1_THERM_RESOLUTION_MIN 9
/* Maximum allowed value */
# define W1_THERM_RESOLUTION_MAX 14
2020-05-11 23:35:35 +03:00
/* Helpers Macros */
2020-05-11 23:37:08 +03:00
/*
* return a pointer on the slave w1_therm_family_converter struct :
* always test family data existence before using this macro
*/
# define SLAVE_SPECIFIC_FUNC(sl) \
( ( ( struct w1_therm_family_data * ) ( sl - > family_data ) ) - > specific_functions )
2020-05-11 23:36:50 +03:00
/*
* return the power mode of the sl slave : 1 - ext , 0 - parasite , < 0 unknown
* always test family data existence before using this macro
*/
# define SLAVE_POWERMODE(sl) \
( ( ( struct w1_therm_family_data * ) ( sl - > family_data ) ) - > external_powered )
2020-05-11 23:37:08 +03:00
/*
* return the resolution in bit of the sl slave : < 0 unknown
* always test family data existence before using this macro
*/
# define SLAVE_RESOLUTION(sl) \
( ( ( struct w1_therm_family_data * ) ( sl - > family_data ) ) - > resolution )
2020-09-04 19:00:03 +03:00
/*
* return the conv_time_override of the sl slave
* always test family data existence before using this macro
*/
# define SLAVE_CONV_TIME_OVERRIDE(sl) \
( ( ( struct w1_therm_family_data * ) ( sl - > family_data ) ) - > conv_time_override )
/*
* return the features of the sl slave
* always test family data existence before using this macro
*/
# define SLAVE_FEATURES(sl) \
( ( ( struct w1_therm_family_data * ) ( sl - > family_data ) ) - > features )
2020-05-11 23:38:20 +03:00
/*
* return whether or not a converT command has been issued to the slave
* * 0 : no bulk read is pending
* * - 1 : conversion is in progress
* * 1 : conversion done , result to be read
*/
# define SLAVE_CONVERT_TRIGGERED(sl) \
( ( ( struct w1_therm_family_data * ) ( sl - > family_data ) ) - > convert_triggered )
2020-05-11 23:35:35 +03:00
/* return the address of the refcnt in the family data */
# define THERM_REFCNT(family_data) \
( & ( ( struct w1_therm_family_data * ) family_data ) - > refcnt )
/* Structs definition */
/**
* struct w1_therm_family_converter - bind device specific functions
* @ broken : flag for non - registred families
* @ reserved : not used here
* @ f : pointer to the device binding structure
* @ convert : pointer to the device conversion function
2020-05-11 23:37:42 +03:00
* @ get_conversion_time : pointer to the device conversion time function
2020-05-11 23:37:08 +03:00
* @ set_resolution : pointer to the device set_resolution function
* @ get_resolution : pointer to the device get_resolution function
2020-05-11 23:38:01 +03:00
* @ write_data : pointer to the device writing function ( 2 or 3 bytes )
2020-05-11 23:38:20 +03:00
* @ bulk_read : true if device family support bulk read , false otherwise
2020-05-11 23:35:35 +03:00
*/
struct w1_therm_family_converter {
u8 broken ;
u16 reserved ;
struct w1_family * f ;
int ( * convert ) ( u8 rom [ 9 ] ) ;
2020-05-11 23:37:42 +03:00
int ( * get_conversion_time ) ( struct w1_slave * sl ) ;
2020-05-11 23:37:08 +03:00
int ( * set_resolution ) ( struct w1_slave * sl , int val ) ;
int ( * get_resolution ) ( struct w1_slave * sl ) ;
2020-05-11 23:38:01 +03:00
int ( * write_data ) ( struct w1_slave * sl , const u8 * data ) ;
2020-05-11 23:38:20 +03:00
bool bulk_read ;
2020-05-11 23:35:35 +03:00
} ;
/**
* struct w1_therm_family_data - device data
* @ rom : ROM device id ( 64 bit Lasered ROM code + 1 CRC byte )
* @ refcnt : ref count
2020-05-11 23:36:50 +03:00
* @ external_powered : 1 device powered externally ,
* 0 device parasite powered ,
* - x error or undefined
2020-05-11 23:37:08 +03:00
* @ resolution : current device resolution
2020-05-11 23:38:20 +03:00
* @ convert_triggered : conversion state of the device
2020-09-04 19:00:03 +03:00
* @ conv_time_override : user selected conversion time or CONV_TIME_DEFAULT
* @ features : bit mask - enable temperature validity check , poll for completion
2020-05-11 23:37:08 +03:00
* @ specific_functions : pointer to struct of device specific function
2020-05-11 23:35:35 +03:00
*/
2015-05-09 03:51:50 +03:00
struct w1_therm_family_data {
uint8_t rom [ 9 ] ;
atomic_t refcnt ;
2020-05-11 23:36:50 +03:00
int external_powered ;
2020-05-11 23:37:08 +03:00
int resolution ;
2020-05-11 23:38:20 +03:00
int convert_triggered ;
2020-09-04 19:00:03 +03:00
int conv_time_override ;
unsigned int features ;
2020-05-11 23:37:08 +03:00
struct w1_therm_family_converter * specific_functions ;
2015-05-09 03:51:50 +03:00
} ;
2020-05-11 23:35:35 +03:00
/**
* struct therm_info - store temperature reading
* @ rom : read device data ( 8 data bytes + 1 CRC byte )
* @ crc : computed crc from rom
* @ verdict : 1 crc checked , 0 crc not matching
*/
2017-08-31 02:34:34 +03:00
struct therm_info {
u8 rom [ 9 ] ;
u8 crc ;
u8 verdict ;
} ;
2020-05-11 23:36:10 +03:00
/* Hardware Functions declaration */
/**
* reset_select_slave ( ) - reset and select a slave
* @ sl : the slave to select
*
* Resets the bus and select the slave by sending a ROM MATCH cmd
* w1_reset_select_slave ( ) from w1_io . c could not be used here because
* it sent a SKIP ROM command if only one device is on the line .
* At the beginning of the such process , sl - > master - > slave_count is 1 even if
* more devices are on the line , causing collision on the line .
*
* Context : The w1 master lock must be held .
*
* Return : 0 if success , negative kernel error code otherwise .
*/
static int reset_select_slave ( struct w1_slave * sl ) ;
2020-05-11 23:37:42 +03:00
/**
* convert_t ( ) - Query the device for temperature conversion and read
* @ sl : pointer to the slave to read
* @ info : pointer to a structure to store the read results
*
* Return : 0 if success , - kernel error code otherwise
*/
static int convert_t ( struct w1_slave * sl , struct therm_info * info ) ;
2020-05-11 23:37:08 +03:00
/**
* read_scratchpad ( ) - read the data in device RAM
* @ sl : pointer to the slave to read
* @ info : pointer to a structure to store the read results
*
* Return : 0 if success , - kernel error code otherwise
*/
static int read_scratchpad ( struct w1_slave * sl , struct therm_info * info ) ;
/**
* write_scratchpad ( ) - write nb_bytes in the device RAM
* @ sl : pointer to the slave to write in
* @ data : pointer to an array of 3 bytes , as 3 bytes MUST be written
* @ nb_bytes : number of bytes to be written ( 2 for DS18S20 , 3 otherwise )
*
* Return : 0 if success , - kernel error code otherwise
*/
static int write_scratchpad ( struct w1_slave * sl , const u8 * data , u8 nb_bytes ) ;
2020-05-11 23:37:25 +03:00
/**
* copy_scratchpad ( ) - Copy the content of scratchpad in device EEPROM
* @ sl : slave involved
*
* Return : 0 if success , - kernel error code otherwise
*/
static int copy_scratchpad ( struct w1_slave * sl ) ;
/**
* recall_eeprom ( ) - Restore EEPROM data to device RAM
* @ sl : slave involved
*
* Return : 0 if success , - kernel error code otherwise
*/
static int recall_eeprom ( struct w1_slave * sl ) ;
2020-05-11 23:36:50 +03:00
/**
* read_powermode ( ) - Query the power mode of the slave
* @ sl : slave to retrieve the power mode
*
* Ask the device to get its power mode ( external or parasite )
* and store the power status in the & struct w1_therm_family_data .
*
* Return :
* * 0 parasite powered device
* * 1 externally powered device
* * < 0 kernel error code
*/
static int read_powermode ( struct w1_slave * sl ) ;
2020-05-11 23:38:20 +03:00
/**
* trigger_bulk_read ( ) - function to trigger a bulk read on the bus
* @ dev_master : the device master of the bus
*
2022-05-21 14:10:16 +03:00
* Send a SKIP ROM follow by a CONVERT T command on the bus .
2020-05-11 23:38:20 +03:00
* It also set the status flag in each slave & struct w1_therm_family_data
* to signal that a conversion is in progress .
*
* Return : 0 if success , - kernel error code otherwise
*/
static int trigger_bulk_read ( struct w1_master * dev_master ) ;
2020-05-11 23:35:35 +03:00
/* Sysfs interface declaration */
2005-04-17 02:20:36 +04:00
2013-08-22 02:44:56 +04:00
static ssize_t w1_slave_show ( struct device * device ,
2008-10-16 09:04:51 +04:00
struct device_attribute * attr , char * buf ) ;
2005-04-17 02:20:36 +04:00
2016-05-02 00:23:33 +03:00
static ssize_t w1_slave_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size ) ;
2015-04-28 14:44:17 +03:00
static ssize_t w1_seq_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
2020-05-11 23:37:42 +03:00
static ssize_t temperature_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
2020-05-11 23:36:50 +03:00
static ssize_t ext_power_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
2020-05-11 23:37:08 +03:00
static ssize_t resolution_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
static ssize_t resolution_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size ) ;
2020-11-12 09:49:31 +03:00
static ssize_t eeprom_cmd_store ( struct device * device ,
2020-05-11 23:37:25 +03:00
struct device_attribute * attr , const char * buf , size_t size ) ;
2020-05-11 23:38:01 +03:00
static ssize_t alarms_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size ) ;
static ssize_t alarms_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
2020-05-11 23:38:20 +03:00
static ssize_t therm_bulk_read_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size ) ;
static ssize_t therm_bulk_read_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
2020-09-04 19:00:03 +03:00
static ssize_t conv_time_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
static ssize_t conv_time_store ( struct device * device ,
struct device_attribute * attr , const char * buf ,
size_t size ) ;
static ssize_t features_show ( struct device * device ,
struct device_attribute * attr , char * buf ) ;
static ssize_t features_store ( struct device * device ,
struct device_attribute * attr , const char * buf ,
size_t size ) ;
2020-05-11 23:35:35 +03:00
/* Attributes declarations */
2016-05-02 00:23:33 +03:00
static DEVICE_ATTR_RW ( w1_slave ) ;
2015-04-28 14:44:17 +03:00
static DEVICE_ATTR_RO ( w1_seq ) ;
2020-05-11 23:37:42 +03:00
static DEVICE_ATTR_RO ( temperature ) ;
2020-05-11 23:36:50 +03:00
static DEVICE_ATTR_RO ( ext_power ) ;
2020-05-11 23:37:08 +03:00
static DEVICE_ATTR_RW ( resolution ) ;
2020-11-12 09:49:31 +03:00
static DEVICE_ATTR_WO ( eeprom_cmd ) ;
2020-05-11 23:38:01 +03:00
static DEVICE_ATTR_RW ( alarms ) ;
2020-09-04 19:00:03 +03:00
static DEVICE_ATTR_RW ( conv_time ) ;
static DEVICE_ATTR_RW ( features ) ;
2005-08-11 17:27:50 +04:00
2020-05-11 23:38:20 +03:00
static DEVICE_ATTR_RW ( therm_bulk_read ) ; /* attribut at master level */
2020-05-11 23:35:35 +03:00
/* Interface Functions declaration */
/**
* w1_therm_add_slave ( ) - Called when a new slave is discovered
* @ sl : slave just discovered by the master .
*
* Called by the master when the slave is discovered on the bus . Used to
* initialize slave state before the beginning of any communication .
*
* Return : 0 - If success , negative kernel code otherwise
*/
static int w1_therm_add_slave ( struct w1_slave * sl ) ;
/**
* w1_therm_remove_slave ( ) - Called when a slave is removed
* @ sl : slave to be removed .
*
* Called by the master when the slave is considered not to be on the bus
* anymore . Used to free memory .
*/
static void w1_therm_remove_slave ( struct w1_slave * sl ) ;
/* Family attributes */
2013-08-22 02:44:56 +04:00
static struct attribute * w1_therm_attrs [ ] = {
2020-05-11 23:37:08 +03:00
& dev_attr_w1_slave . attr ,
2020-05-11 23:37:42 +03:00
& dev_attr_temperature . attr ,
2020-05-11 23:37:08 +03:00
& dev_attr_ext_power . attr ,
& dev_attr_resolution . attr ,
2020-11-12 09:49:31 +03:00
& dev_attr_eeprom_cmd . attr ,
2020-05-11 23:38:01 +03:00
& dev_attr_alarms . attr ,
2020-09-04 19:00:03 +03:00
& dev_attr_conv_time . attr ,
& dev_attr_features . attr ,
2020-05-11 23:37:08 +03:00
NULL ,
} ;
static struct attribute * w1_ds18s20_attrs [ ] = {
2013-08-22 02:44:56 +04:00
& dev_attr_w1_slave . attr ,
2020-05-11 23:37:42 +03:00
& dev_attr_temperature . attr ,
2020-05-11 23:36:50 +03:00
& dev_attr_ext_power . attr ,
2020-11-12 09:49:31 +03:00
& dev_attr_eeprom_cmd . attr ,
2020-05-11 23:38:01 +03:00
& dev_attr_alarms . attr ,
2020-09-04 19:00:03 +03:00
& dev_attr_conv_time . attr ,
& dev_attr_features . attr ,
2013-08-22 02:44:56 +04:00
NULL ,
} ;
2015-04-28 14:44:17 +03:00
static struct attribute * w1_ds28ea00_attrs [ ] = {
& dev_attr_w1_slave . attr ,
& dev_attr_w1_seq . attr ,
2020-05-11 23:37:42 +03:00
& dev_attr_temperature . attr ,
2020-05-11 23:36:50 +03:00
& dev_attr_ext_power . attr ,
2020-05-11 23:37:08 +03:00
& dev_attr_resolution . attr ,
2020-11-12 09:49:31 +03:00
& dev_attr_eeprom_cmd . attr ,
2020-05-11 23:38:01 +03:00
& dev_attr_alarms . attr ,
2020-09-04 19:00:03 +03:00
& dev_attr_conv_time . attr ,
& dev_attr_features . attr ,
2015-04-28 14:44:17 +03:00
NULL ,
} ;
2017-08-31 02:34:35 +03:00
2020-05-11 23:35:35 +03:00
/* Attribute groups */
2013-08-22 02:44:56 +04:00
ATTRIBUTE_GROUPS ( w1_therm ) ;
2020-05-11 23:37:08 +03:00
ATTRIBUTE_GROUPS ( w1_ds18s20 ) ;
2015-04-28 14:44:17 +03:00
ATTRIBUTE_GROUPS ( w1_ds28ea00 ) ;
2005-08-11 17:27:50 +04:00
2017-08-31 02:34:35 +03:00
# if IS_REACHABLE(CONFIG_HWMON)
static int w1_read_temp ( struct device * dev , u32 attr , int channel ,
long * val ) ;
static umode_t w1_is_visible ( const void * _data , enum hwmon_sensor_types type ,
u32 attr , int channel )
{
return attr = = hwmon_temp_input ? 0444 : 0 ;
}
static int w1_read ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
{
switch ( type ) {
case hwmon_temp :
return w1_read_temp ( dev , attr , channel , val ) ;
default :
return - EOPNOTSUPP ;
}
}
static const u32 w1_temp_config [ ] = {
HWMON_T_INPUT ,
0
} ;
static const struct hwmon_channel_info w1_temp = {
. type = hwmon_temp ,
. config = w1_temp_config ,
} ;
2023-04-07 18:01:21 +03:00
static const struct hwmon_channel_info * const w1_info [ ] = {
2017-08-31 02:34:35 +03:00
& w1_temp ,
NULL
} ;
static const struct hwmon_ops w1_hwmon_ops = {
. is_visible = w1_is_visible ,
. read = w1_read ,
} ;
static const struct hwmon_chip_info w1_chip_info = {
. ops = & w1_hwmon_ops ,
. info = w1_info ,
} ;
# define W1_CHIPINFO (&w1_chip_info)
# else
# define W1_CHIPINFO NULL
# endif
2020-05-11 23:35:35 +03:00
/* Family operations */
2020-10-04 22:32:01 +03:00
static const struct w1_family_ops w1_therm_fops = {
2014-01-16 08:29:24 +04:00
. add_slave = w1_therm_add_slave ,
. remove_slave = w1_therm_remove_slave ,
2013-08-22 02:44:56 +04:00
. groups = w1_therm_groups ,
2017-08-31 02:34:35 +03:00
. chip_info = W1_CHIPINFO ,
2005-04-17 02:20:36 +04:00
} ;
2020-10-04 22:32:01 +03:00
static const struct w1_family_ops w1_ds18s20_fops = {
2020-05-11 23:37:08 +03:00
. add_slave = w1_therm_add_slave ,
. remove_slave = w1_therm_remove_slave ,
. groups = w1_ds18s20_groups ,
. chip_info = W1_CHIPINFO ,
} ;
2020-10-04 22:32:01 +03:00
static const struct w1_family_ops w1_ds28ea00_fops = {
2015-04-28 14:44:17 +03:00
. add_slave = w1_therm_add_slave ,
. remove_slave = w1_therm_remove_slave ,
. groups = w1_ds28ea00_groups ,
2017-08-31 02:34:35 +03:00
. chip_info = W1_CHIPINFO ,
2015-04-28 14:44:17 +03:00
} ;
2020-05-11 23:35:35 +03:00
/* Family binding operations struct */
2005-04-17 22:58:14 +04:00
static struct w1_family w1_therm_family_DS18S20 = {
. fid = W1_THERM_DS18S20 ,
2020-05-11 23:37:08 +03:00
. fops = & w1_ds18s20_fops ,
2005-04-17 22:58:14 +04:00
} ;
static struct w1_family w1_therm_family_DS18B20 = {
. fid = W1_THERM_DS18B20 ,
. fops = & w1_therm_fops ,
} ;
2005-08-11 17:27:50 +04:00
2005-04-17 22:58:14 +04:00
static struct w1_family w1_therm_family_DS1822 = {
. fid = W1_THERM_DS1822 ,
. fops = & w1_therm_fops ,
} ;
2011-07-27 03:08:55 +04:00
static struct w1_family w1_therm_family_DS28EA00 = {
. fid = W1_THERM_DS28EA00 ,
2015-04-28 14:44:17 +03:00
. fops = & w1_ds28ea00_fops ,
2011-07-27 03:08:55 +04:00
} ;
2012-08-16 20:56:40 +04:00
static struct w1_family w1_therm_family_DS1825 = {
. fid = W1_THERM_DS1825 ,
. fops = & w1_therm_fops ,
} ;
2020-05-11 23:35:35 +03:00
/* Device dependent func */
2005-04-17 22:58:14 +04:00
2020-05-11 23:37:42 +03:00
static inline int w1_DS18B20_convert_time ( struct w1_slave * sl )
{
int ret ;
if ( ! sl - > family_data )
return - ENODEV ; /* device unknown */
2020-09-04 19:00:03 +03:00
if ( SLAVE_CONV_TIME_OVERRIDE ( sl ) ! = CONV_TIME_DEFAULT )
return SLAVE_CONV_TIME_OVERRIDE ( sl ) ;
2020-09-04 19:00:04 +03:00
/* Return the conversion time, depending on resolution,
* select maximum conversion time among all compatible devices
*/
2020-05-11 23:37:42 +03:00
switch ( SLAVE_RESOLUTION ( sl ) ) {
case 9 :
ret = 95 ;
break ;
case 10 :
ret = 190 ;
break ;
case 11 :
ret = 375 ;
break ;
case 12 :
2020-09-04 19:00:04 +03:00
ret = 750 ;
break ;
case 13 :
ret = 850 ; /* GX20MH01 only. Datasheet says 500ms, but that's not enough. */
break ;
case 14 :
ret = 1600 ; /* GX20MH01 only. Datasheet says 1000ms - not enough */
break ;
2020-05-11 23:37:42 +03:00
default :
ret = 750 ;
}
return ret ;
}
static inline int w1_DS18S20_convert_time ( struct w1_slave * sl )
{
2020-09-04 19:00:03 +03:00
if ( ! sl - > family_data )
return - ENODEV ; /* device unknown */
if ( SLAVE_CONV_TIME_OVERRIDE ( sl ) = = CONV_TIME_DEFAULT )
return 750 ; /* default for DS18S20 */
else
return SLAVE_CONV_TIME_OVERRIDE ( sl ) ;
2020-05-11 23:37:42 +03:00
}
2022-03-06 17:58:08 +03:00
static inline int w1_DS1825_convert_time ( struct w1_slave * sl )
{
int ret ;
if ( ! sl - > family_data )
return - ENODEV ; /* device unknown */
if ( SLAVE_CONV_TIME_OVERRIDE ( sl ) ! = CONV_TIME_DEFAULT )
return SLAVE_CONV_TIME_OVERRIDE ( sl ) ;
/* Return the conversion time, depending on resolution,
* select maximum conversion time among all compatible devices
*/
switch ( SLAVE_RESOLUTION ( sl ) ) {
case 9 :
ret = 95 ;
break ;
case 10 :
ret = 190 ;
break ;
case 11 :
ret = 375 ;
break ;
case 12 :
ret = 750 ;
break ;
case 14 :
ret = 100 ; /* MAX31850 only. Datasheet says 100ms */
break ;
default :
ret = 750 ;
}
return ret ;
}
2020-05-11 23:37:08 +03:00
static inline int w1_DS18B20_write_data ( struct w1_slave * sl ,
const u8 * data )
2016-05-02 00:23:33 +03:00
{
2020-05-11 23:37:08 +03:00
return write_scratchpad ( sl , data , 3 ) ;
2016-05-02 00:23:33 +03:00
}
2020-05-11 23:37:08 +03:00
static inline int w1_DS18S20_write_data ( struct w1_slave * sl ,
const u8 * data )
2016-05-02 00:23:33 +03:00
{
2020-05-11 23:37:08 +03:00
/* No config register */
return write_scratchpad ( sl , data , 2 ) ;
}
2016-05-02 00:23:33 +03:00
2020-05-11 23:37:08 +03:00
static inline int w1_DS18B20_set_resolution ( struct w1_slave * sl , int val )
{
2020-05-19 18:45:53 +03:00
int ret ;
2020-09-04 19:00:03 +03:00
struct therm_info info , info2 ;
2016-05-02 00:23:33 +03:00
2020-09-04 19:00:04 +03:00
/* DS18B20 resolution is 9 to 12 bits */
/* GX20MH01 resolution is 9 to 14 bits */
2022-03-06 17:58:08 +03:00
/* MAX31850 resolution is fixed 14 bits */
2020-09-04 19:00:04 +03:00
if ( val < W1_THERM_RESOLUTION_MIN | | val > W1_THERM_RESOLUTION_MAX )
2020-05-11 23:37:08 +03:00
return - EINVAL ;
2020-09-04 19:00:04 +03:00
/* Calc bit value from resolution */
val = ( val - W1_THERM_RESOLUTION_MIN ) < < W1_THERM_RESOLUTION_SHIFT ;
2020-05-11 23:37:08 +03:00
/*
* Read the scratchpad to change only the required bits
* ( bit5 & bit 6 from byte 4 )
*/
ret = read_scratchpad ( sl , & info ) ;
2020-09-04 19:00:04 +03:00
if ( ret )
2020-05-11 23:37:08 +03:00
return ret ;
2016-05-02 00:23:33 +03:00
2020-09-04 19:00:04 +03:00
info . rom [ 4 ] & = ~ W1_THERM_RESOLUTION_MASK ;
info . rom [ 4 ] | = val ;
2020-05-11 23:37:08 +03:00
/* Write data in the device RAM */
2020-09-04 19:00:04 +03:00
ret = w1_DS18B20_write_data ( sl , info . rom + 2 ) ;
2020-09-04 19:00:03 +03:00
if ( ret )
return ret ;
2016-05-02 00:23:33 +03:00
2020-09-04 19:00:04 +03:00
/* Have to read back the resolution to verify an actual value
* GX20MH01 and DS18B20 are indistinguishable by family number , but resolutions differ
* Some DS18B20 clones don ' t support resolution change
*/
2020-09-04 19:00:03 +03:00
ret = read_scratchpad ( sl , & info2 ) ;
if ( ret )
2020-09-04 19:00:04 +03:00
/* Scratchpad read fail */
2020-09-04 19:00:03 +03:00
return ret ;
2020-09-04 19:00:04 +03:00
if ( ( info2 . rom [ 4 ] & W1_THERM_RESOLUTION_MASK ) = = ( info . rom [ 4 ] & W1_THERM_RESOLUTION_MASK ) )
2020-09-04 19:00:03 +03:00
return 0 ;
2020-09-04 19:00:04 +03:00
/* Resolution verify error */
return - EIO ;
2020-05-11 23:37:08 +03:00
}
2016-05-02 00:23:33 +03:00
2020-05-11 23:37:08 +03:00
static inline int w1_DS18B20_get_resolution ( struct w1_slave * sl )
{
2020-05-19 18:45:53 +03:00
int ret ;
2020-09-04 19:00:04 +03:00
int resolution ;
2020-05-11 23:37:08 +03:00
struct therm_info info ;
2016-05-02 00:23:33 +03:00
2020-05-11 23:37:08 +03:00
ret = read_scratchpad ( sl , & info ) ;
2016-05-02 00:23:33 +03:00
2020-09-04 19:00:04 +03:00
if ( ret )
return ret ;
resolution = ( ( info . rom [ 4 ] & W1_THERM_RESOLUTION_MASK ) > > W1_THERM_RESOLUTION_SHIFT )
+ W1_THERM_RESOLUTION_MIN ;
/* GX20MH01 has one special case:
* > = 14 means 14 bits when getting resolution from bit value .
2022-03-06 17:58:08 +03:00
* MAX31850 delivers fixed 15 and has 14 bits .
2020-09-04 19:00:04 +03:00
* Other devices have no more then 12 bits .
*/
if ( resolution > W1_THERM_RESOLUTION_MAX )
resolution = W1_THERM_RESOLUTION_MAX ;
return resolution ;
2016-05-02 00:23:33 +03:00
}
2020-05-11 23:35:35 +03:00
/**
* w1_DS18B20_convert_temp ( ) - temperature computation for DS18B20
* @ rom : data read from device RAM ( 8 data bytes + 1 CRC byte )
*
* Can be called for any DS18B20 compliant device .
*
* Return : value in millidegrees Celsius .
*/
2005-04-17 22:58:14 +04:00
static inline int w1_DS18B20_convert_temp ( u8 rom [ 9 ] )
{
2021-01-21 12:30:21 +03:00
u16 bv ;
s16 t ;
/* Signed 16-bit value to unsigned, cpu order */
bv = le16_to_cpup ( ( __le16 * ) rom ) ;
2016-07-22 07:33:35 +03:00
2020-09-04 19:00:04 +03:00
/* Config register bit R2 = 1 - GX20MH01 in 13 or 14 bit resolution mode */
if ( rom [ 4 ] & 0x80 ) {
/* Insert two temperature bits from config register */
/* Avoid arithmetic shift of signed value */
bv = ( bv < < 2 ) | ( rom [ 4 ] & 3 ) ;
2021-01-21 12:30:21 +03:00
t = ( s16 ) bv ; /* Degrees, lowest bit is 2^-6 */
return ( int ) t * 1000 / 64 ; /* Sign-extend to int; millidegrees */
2020-09-04 19:00:04 +03:00
}
2021-01-21 12:30:21 +03:00
t = ( s16 ) bv ; /* Degrees, lowest bit is 2^-4 */
return ( int ) t * 1000 / 16 ; /* Sign-extend to int; millidegrees */
2005-04-17 22:58:14 +04:00
}
2020-05-11 23:35:35 +03:00
/**
* w1_DS18S20_convert_temp ( ) - temperature computation for DS18S20
* @ rom : data read from device RAM ( 8 data bytes + 1 CRC byte )
*
* Can be called for any DS18S20 compliant device .
*
* Return : value in millidegrees Celsius .
*/
2005-04-17 22:58:14 +04:00
static inline int w1_DS18S20_convert_temp ( u8 rom [ 9 ] )
2005-04-17 02:20:36 +04:00
{
int t , h ;
2005-04-17 22:58:14 +04:00
2020-05-11 23:37:42 +03:00
if ( ! rom [ 7 ] ) {
pr_debug ( " %s: Invalid argument for conversion \n " , __func__ ) ;
2005-04-17 22:58:14 +04:00
return 0 ;
2020-05-11 23:37:42 +03:00
}
2005-12-06 13:38:28 +03:00
2005-04-17 02:20:36 +04:00
if ( rom [ 1 ] = = 0 )
t = ( ( s32 ) rom [ 0 ] > > 1 ) * 1000 ;
else
t = 1000 * ( - 1 * ( s32 ) ( 0x100 - rom [ 0 ] ) > > 1 ) ;
2005-12-06 13:38:28 +03:00
2005-04-17 02:20:36 +04:00
t - = 250 ;
h = 1000 * ( ( s32 ) rom [ 7 ] - ( s32 ) rom [ 6 ] ) ;
h / = ( s32 ) rom [ 7 ] ;
t + = h ;
return t ;
}
2022-03-06 17:58:08 +03:00
/**
* w1_DS1825_convert_temp ( ) - temperature computation for DS1825
* @ rom : data read from device RAM ( 8 data bytes + 1 CRC byte )
*
* Can be called for any DS1825 compliant device .
* Is used by MAX31850 , too
*
* Return : value in millidegrees Celsius .
*/
static inline int w1_DS1825_convert_temp ( u8 rom [ 9 ] )
{
u16 bv ;
s16 t ;
/* Signed 16-bit value to unsigned, cpu order */
bv = le16_to_cpup ( ( __le16 * ) rom ) ;
/* Config register bit 7 = 1 - MA31850 found, 14 bit resolution */
if ( rom [ 4 ] & 0x80 ) {
/* Mask out bits 0 (Fault) and 1 (Reserved) */
/* Avoid arithmetic shift of signed value */
bv = ( bv & 0xFFFC ) ; /* Degrees, lowest 4 bits are 2^-1, 2^-2 and 2 zero bits */
}
t = ( s16 ) bv ; /* Degrees, lowest bit is 2^-4 */
return ( int ) t * 1000 / 16 ; /* Sign-extend to int; millidegrees */
}
2020-05-11 23:35:35 +03:00
/* Device capability description */
2020-09-04 19:00:04 +03:00
/* GX20MH01 device shares family number and structure with DS18B20 */
2020-05-11 23:35:35 +03:00
static struct w1_therm_family_converter w1_therm_families [ ] = {
{
2020-05-11 23:37:42 +03:00
. f = & w1_therm_family_DS18S20 ,
. convert = w1_DS18S20_convert_temp ,
. get_conversion_time = w1_DS18S20_convert_time ,
. set_resolution = NULL , /* no config register */
. get_resolution = NULL , /* no config register */
2020-05-11 23:38:01 +03:00
. write_data = w1_DS18S20_write_data ,
2020-05-11 23:38:20 +03:00
. bulk_read = true
2020-05-11 23:35:35 +03:00
} ,
{
2020-05-11 23:37:42 +03:00
. f = & w1_therm_family_DS1822 ,
. convert = w1_DS18B20_convert_temp ,
. get_conversion_time = w1_DS18B20_convert_time ,
. set_resolution = w1_DS18B20_set_resolution ,
. get_resolution = w1_DS18B20_get_resolution ,
2020-05-11 23:38:01 +03:00
. write_data = w1_DS18B20_write_data ,
2020-05-11 23:38:20 +03:00
. bulk_read = true
2020-05-11 23:35:35 +03:00
} ,
{
2020-09-04 19:00:04 +03:00
/* Also used for GX20MH01 */
2020-05-11 23:37:42 +03:00
. f = & w1_therm_family_DS18B20 ,
. convert = w1_DS18B20_convert_temp ,
. get_conversion_time = w1_DS18B20_convert_time ,
. set_resolution = w1_DS18B20_set_resolution ,
. get_resolution = w1_DS18B20_get_resolution ,
2020-05-11 23:38:01 +03:00
. write_data = w1_DS18B20_write_data ,
2020-05-11 23:38:20 +03:00
. bulk_read = true
2020-05-11 23:35:35 +03:00
} ,
{
2020-05-11 23:37:42 +03:00
. f = & w1_therm_family_DS28EA00 ,
. convert = w1_DS18B20_convert_temp ,
. get_conversion_time = w1_DS18B20_convert_time ,
. set_resolution = w1_DS18B20_set_resolution ,
. get_resolution = w1_DS18B20_get_resolution ,
2020-05-11 23:38:01 +03:00
. write_data = w1_DS18B20_write_data ,
2020-05-11 23:38:20 +03:00
. bulk_read = false
2020-05-11 23:35:35 +03:00
} ,
{
2022-03-06 17:58:08 +03:00
/* Also used for MAX31850 */
2020-05-11 23:37:42 +03:00
. f = & w1_therm_family_DS1825 ,
2022-03-06 17:58:08 +03:00
. convert = w1_DS1825_convert_temp ,
. get_conversion_time = w1_DS1825_convert_time ,
2020-05-11 23:37:42 +03:00
. set_resolution = w1_DS18B20_set_resolution ,
. get_resolution = w1_DS18B20_get_resolution ,
2020-05-11 23:38:01 +03:00
. write_data = w1_DS18B20_write_data ,
2020-05-11 23:38:20 +03:00
. bulk_read = true
2020-05-11 23:35:35 +03:00
}
} ;
/* Helpers Functions */
2020-05-11 23:37:08 +03:00
/**
* device_family ( ) - Retrieve a pointer on & struct w1_therm_family_converter
* @ sl : slave to retrieve the device specific structure
*
* Return : pointer to the slaves ' s family converter , NULL if not known
*/
static struct w1_therm_family_converter * device_family ( struct w1_slave * sl )
{
struct w1_therm_family_converter * ret = NULL ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( w1_therm_families ) ; + + i ) {
if ( w1_therm_families [ i ] . f - > fid = = sl - > family - > fid ) {
ret = & w1_therm_families [ i ] ;
break ;
}
}
return ret ;
}
2020-05-11 23:36:50 +03:00
/**
* bus_mutex_lock ( ) - Acquire the mutex
* @ lock : w1 bus mutex to acquire
*
* It try to acquire the mutex W1_THERM_MAX_TRY times and wait
* W1_THERM_RETRY_DELAY between 2 attempts .
*
* Return : true is mutex is acquired and lock , false otherwise
*/
static inline bool bus_mutex_lock ( struct mutex * lock )
{
int max_trying = W1_THERM_MAX_TRY ;
/* try to acquire the mutex, if not, sleep retry_delay before retry) */
while ( mutex_lock_interruptible ( lock ) ! = 0 & & max_trying > 0 ) {
unsigned long sleep_rem ;
sleep_rem = msleep_interruptible ( W1_THERM_RETRY_DELAY ) ;
if ( ! sleep_rem )
max_trying - - ;
}
if ( ! max_trying )
return false ; /* Didn't acquire the bus mutex */
return true ;
}
2020-09-04 19:00:03 +03:00
/**
* check_family_data ( ) - Check if family data and specific functions are present
* @ sl : W1 device data
*
* Return : 0 - OK , negative value - error
*/
static int check_family_data ( struct w1_slave * sl )
{
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( & sl - > dev ,
" %s: Device is not supported by the driver \n " , __func__ ) ;
return - EINVAL ; /* No device family */
}
return 0 ;
}
2020-05-11 23:38:20 +03:00
/**
2021-05-18 08:04:01 +03:00
* bulk_read_support ( ) - check if slave support bulk read
2020-05-11 23:38:20 +03:00
* @ sl : device to check the ability
*
* Return : true if bulk read is supported , false if not or error
*/
static inline bool bulk_read_support ( struct w1_slave * sl )
{
if ( SLAVE_SPECIFIC_FUNC ( sl ) )
return SLAVE_SPECIFIC_FUNC ( sl ) - > bulk_read ;
dev_info ( & sl - > dev ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return false ; /* No device family */
}
2020-05-11 23:35:35 +03:00
/**
2020-05-11 23:37:42 +03:00
* conversion_time ( ) - get the Tconv for the slave
* @ sl : device to get the conversion time
2020-05-11 23:35:35 +03:00
*
2020-05-11 23:37:42 +03:00
* On device supporting resolution settings , conversion time depend
* on the resolution setting . This helper function get the slave timing ,
* depending on its current setting .
2020-05-11 23:35:35 +03:00
*
2020-05-11 23:37:42 +03:00
* Return : conversion time in ms , negative values are kernel error code
2020-05-11 23:35:35 +03:00
*/
2020-05-11 23:37:42 +03:00
static inline int conversion_time ( struct w1_slave * sl )
2005-04-17 22:58:14 +04:00
{
2020-05-11 23:37:42 +03:00
if ( SLAVE_SPECIFIC_FUNC ( sl ) )
return SLAVE_SPECIFIC_FUNC ( sl ) - > get_conversion_time ( sl ) ;
2005-04-17 22:58:14 +04:00
2020-05-11 23:37:42 +03:00
dev_info ( & sl - > dev ,
" %s: Device not supported by the driver \n " , __func__ ) ;
2005-04-17 22:58:14 +04:00
2020-05-11 23:37:42 +03:00
return - ENODEV ; /* No device family */
}
/**
* temperature_from_RAM ( ) - Convert the read info to temperature
* @ sl : device that sent the RAM data
* @ rom : read value on the slave device RAM
*
* Device dependent , the function bind the correct computation method .
*
* Return : temperature in 1 / 1000 degC , 0 on error .
*/
static inline int temperature_from_RAM ( struct w1_slave * sl , u8 rom [ 9 ] )
{
if ( SLAVE_SPECIFIC_FUNC ( sl ) )
return SLAVE_SPECIFIC_FUNC ( sl ) - > convert ( rom ) ;
dev_info ( & sl - > dev ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return 0 ; /* No device family */
2005-04-17 22:58:14 +04:00
}
2020-05-11 23:38:01 +03:00
/**
* int_to_short ( ) - Safe casting of int to short
*
* @ i : integer to be converted to short
*
* Device register use 1 byte to store signed integer .
* This helper function convert the int in a signed short ,
* using the min / max values that device can measure as limits .
* min / max values are defined by macro .
*
* Return : a short in the range of min / max value
*/
static inline s8 int_to_short ( int i )
{
/* Prepare to cast to short by eliminating out of range values */
2021-03-09 19:06:52 +03:00
i = clamp ( i , MIN_TEMP , MAX_TEMP ) ;
2020-05-11 23:38:01 +03:00
return ( s8 ) i ;
}
2020-05-11 23:35:35 +03:00
/* Interface Functions */
static int w1_therm_add_slave ( struct w1_slave * sl )
2016-05-02 00:23:33 +03:00
{
2020-05-11 23:37:08 +03:00
struct w1_therm_family_converter * sl_family_conv ;
/* Allocate memory */
2020-05-11 23:35:35 +03:00
sl - > family_data = kzalloc ( sizeof ( struct w1_therm_family_data ) ,
GFP_KERNEL ) ;
if ( ! sl - > family_data )
return - ENOMEM ;
2020-05-11 23:36:50 +03:00
2020-05-11 23:35:35 +03:00
atomic_set ( THERM_REFCNT ( sl - > family_data ) , 1 ) ;
2020-05-11 23:36:50 +03:00
2020-05-11 23:37:08 +03:00
/* Get a pointer to the device specific function struct */
sl_family_conv = device_family ( sl ) ;
if ( ! sl_family_conv ) {
kfree ( sl - > family_data ) ;
return - ENODEV ;
}
/* save this pointer to the device structure */
SLAVE_SPECIFIC_FUNC ( sl ) = sl_family_conv ;
2020-05-11 23:38:20 +03:00
if ( bulk_read_support ( sl ) ) {
/*
* add the sys entry to trigger bulk_read
* at master level only the 1 st time
*/
if ( ! bulk_read_device_counter ) {
int err = device_create_file ( & sl - > master - > dev ,
& dev_attr_therm_bulk_read ) ;
if ( err )
dev_warn ( & sl - > dev ,
" %s: Device has been added, but bulk read is unavailable. err=%d \n " ,
__func__ , err ) ;
}
/* Increment the counter */
bulk_read_device_counter + + ;
}
2020-05-11 23:36:50 +03:00
/* Getting the power mode of the device {external, parasite} */
SLAVE_POWERMODE ( sl ) = read_powermode ( sl ) ;
if ( SLAVE_POWERMODE ( sl ) < 0 ) {
/* no error returned as device has been added */
dev_warn ( & sl - > dev ,
" %s: Device has been added, but power_mode may be corrupted. err=%d \n " ,
__func__ , SLAVE_POWERMODE ( sl ) ) ;
}
2020-05-11 23:37:08 +03:00
/* Getting the resolution of the device */
if ( SLAVE_SPECIFIC_FUNC ( sl ) - > get_resolution ) {
SLAVE_RESOLUTION ( sl ) =
SLAVE_SPECIFIC_FUNC ( sl ) - > get_resolution ( sl ) ;
if ( SLAVE_RESOLUTION ( sl ) < 0 ) {
/* no error returned as device has been added */
dev_warn ( & sl - > dev ,
" %s:Device has been added, but resolution may be corrupted. err=%d \n " ,
__func__ , SLAVE_RESOLUTION ( sl ) ) ;
}
}
2020-05-11 23:38:20 +03:00
/* Finally initialize convert_triggered flag */
SLAVE_CONVERT_TRIGGERED ( sl ) = 0 ;
2020-05-11 23:35:35 +03:00
return 0 ;
}
2016-05-02 00:23:33 +03:00
2020-05-11 23:35:35 +03:00
static void w1_therm_remove_slave ( struct w1_slave * sl )
{
int refcnt = atomic_sub_return ( 1 , THERM_REFCNT ( sl - > family_data ) ) ;
2016-05-02 00:23:33 +03:00
2020-05-11 23:38:20 +03:00
if ( bulk_read_support ( sl ) ) {
bulk_read_device_counter - - ;
/* Delete the entry if no more device support the feature */
if ( ! bulk_read_device_counter )
device_remove_file ( & sl - > master - > dev ,
& dev_attr_therm_bulk_read ) ;
}
2020-05-11 23:35:35 +03:00
while ( refcnt ) {
msleep ( 1000 ) ;
refcnt = atomic_read ( THERM_REFCNT ( sl - > family_data ) ) ;
2016-05-02 00:23:33 +03:00
}
2020-05-11 23:35:35 +03:00
kfree ( sl - > family_data ) ;
sl - > family_data = NULL ;
2016-05-02 00:23:33 +03:00
}
2005-04-17 02:20:36 +04:00
2020-05-11 23:35:35 +03:00
/* Hardware Functions */
2020-05-11 23:36:10 +03:00
/* Safe version of reset_select_slave - avoid using the one in w_io.c */
static int reset_select_slave ( struct w1_slave * sl )
{
u8 match [ 9 ] = { W1_MATCH_ROM , } ;
u64 rn = le64_to_cpu ( * ( ( u64 * ) & sl - > reg_num ) ) ;
if ( w1_reset_bus ( sl - > master ) )
return - ENODEV ;
memcpy ( & match [ 1 ] , & rn , 8 ) ;
w1_write_block ( sl - > master , match , 9 ) ;
return 0 ;
}
2020-09-04 19:00:03 +03:00
/**
* w1_poll_completion - Poll for operation completion , with timeout
* @ dev_master : the device master of the bus
* @ tout_ms : timeout in milliseconds
*
* The device is answering 0 ' s while an operation is in progress and 1 ' s after it completes
* Timeout may happen if the previous command was not recognised due to a line noise
*
* Return : 0 - OK , negative error - timeout
*/
2020-10-05 15:37:03 +03:00
static int w1_poll_completion ( struct w1_master * dev_master , int tout_ms )
2020-09-04 19:00:03 +03:00
{
int i ;
for ( i = 0 ; i < tout_ms / W1_POLL_PERIOD ; i + + ) {
/* Delay is before poll, for device to recognize a command */
msleep ( W1_POLL_PERIOD ) ;
/* Compare all 8 bits to mitigate a noise on the bus */
if ( w1_read_8 ( dev_master ) = = 0xFF )
break ;
}
if ( i = = tout_ms / W1_POLL_PERIOD )
return - EIO ;
return 0 ;
}
2020-05-11 23:37:42 +03:00
static int convert_t ( struct w1_slave * sl , struct therm_info * info )
2005-04-17 02:20:36 +04:00
{
2020-05-11 23:37:42 +03:00
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int t_conv ;
int ret = - ENODEV ;
bool strong_pullup ;
2015-05-09 03:51:50 +03:00
2020-05-11 23:37:42 +03:00
if ( ! sl - > family_data )
2017-10-22 01:03:44 +03:00
goto error ;
2005-04-17 02:20:36 +04:00
2020-05-11 23:37:42 +03:00
strong_pullup = ( w1_strong_pullup = = 2 | |
( ! SLAVE_POWERMODE ( sl ) & &
w1_strong_pullup ) ) ;
2017-10-22 01:03:44 +03:00
2020-09-04 19:00:03 +03:00
if ( strong_pullup & & SLAVE_FEATURES ( sl ) & W1_THERM_POLL_COMPLETION ) {
dev_warn ( & sl - > dev ,
" %s: Disabling W1_THERM_POLL_COMPLETION in parasite power mode. \n " ,
__func__ ) ;
SLAVE_FEATURES ( sl ) & = ~ W1_THERM_POLL_COMPLETION ;
}
2020-05-11 23:37:42 +03:00
/* get conversion duration device and id dependent */
t_conv = conversion_time ( sl ) ;
2017-10-22 01:03:44 +03:00
2017-08-31 02:34:34 +03:00
memset ( info - > rom , 0 , sizeof ( info - > rom ) ) ;
2005-04-17 02:20:36 +04:00
2020-05-11 23:37:42 +03:00
/* prevent the slave from going away in sleep */
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto dec_refcnt ;
}
while ( max_trying - - & & ret ) { /* ret should be 0 */
2012-12-18 05:37:56 +04:00
2017-08-31 02:34:34 +03:00
info - > verdict = 0 ;
info - > crc = 0 ;
2020-05-11 23:37:42 +03:00
/* safe version to select slave */
2020-05-11 23:36:10 +03:00
if ( ! reset_select_slave ( sl ) ) {
2011-11-16 03:43:16 +04:00
unsigned long sleep_rem ;
2008-10-16 09:04:43 +04:00
/* 750ms strong pullup (or delay) after the convert */
2020-05-11 23:37:42 +03:00
if ( strong_pullup )
w1_next_pullup ( dev_master , t_conv ) ;
2011-11-16 03:43:16 +04:00
2020-05-11 23:37:42 +03:00
w1_write_8 ( dev_master , W1_CONVERT_TEMP ) ;
2011-11-16 03:43:16 +04:00
2023-04-27 14:21:52 +03:00
if ( SLAVE_FEATURES ( sl ) & W1_THERM_POLL_COMPLETION ) {
ret = w1_poll_completion ( dev_master , W1_POLL_CONVERT_TEMP ) ;
if ( ret ) {
dev_dbg ( & sl - > dev , " %s: Timeout \n " , __func__ ) ;
goto mt_unlock ;
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
} else if ( ! strong_pullup ) { /*no device need pullup */
2020-05-11 23:37:42 +03:00
sleep_rem = msleep_interruptible ( t_conv ) ;
2015-05-09 03:51:50 +03:00
if ( sleep_rem ! = 0 ) {
ret = - EINTR ;
2020-05-11 23:37:42 +03:00
goto mt_unlock ;
2015-05-09 03:51:50 +03:00
}
2020-05-11 23:37:42 +03:00
mutex_unlock ( & dev_master - > bus_mutex ) ;
2023-04-27 14:21:52 +03:00
} else { /*some device need pullup */
mutex_unlock ( & dev_master - > bus_mutex ) ;
sleep_rem = msleep_interruptible ( t_conv ) ;
if ( sleep_rem ! = 0 ) {
ret = - EINTR ;
goto dec_refcnt ;
2011-11-16 03:43:16 +04:00
}
}
2020-05-11 23:37:42 +03:00
ret = read_scratchpad ( sl , info ) ;
2020-09-04 19:00:03 +03:00
/* If enabled, check for conversion success */
if ( ( SLAVE_FEATURES ( sl ) & W1_THERM_CHECK_RESULT ) & &
( info - > rom [ 6 ] = = 0xC ) & &
( ( info - > rom [ 1 ] = = 0x5 & & info - > rom [ 0 ] = = 0x50 ) | |
( info - > rom [ 1 ] = = 0x7 & & info - > rom [ 0 ] = = 0xFF ) )
) {
/* Invalid reading (scratchpad byte 6 = 0xC)
* due to insufficient conversion time
* or power failure .
*/
ret = - EIO ;
}
2020-05-11 23:37:42 +03:00
goto dec_refcnt ;
2005-04-17 02:20:36 +04:00
}
}
2017-08-31 02:34:34 +03:00
mt_unlock :
2020-05-11 23:37:42 +03:00
mutex_unlock ( & dev_master - > bus_mutex ) ;
2017-10-22 01:03:44 +03:00
dec_refcnt :
2020-05-11 23:37:42 +03:00
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
2017-08-31 02:34:34 +03:00
error :
return ret ;
}
2020-09-04 19:00:03 +03:00
static int conv_time_measure ( struct w1_slave * sl , int * conv_time )
{
struct therm_info inf ,
* info = & inf ;
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int ret = - ENODEV ;
bool strong_pullup ;
if ( ! sl - > family_data )
goto error ;
strong_pullup = ( w1_strong_pullup = = 2 | |
( ! SLAVE_POWERMODE ( sl ) & &
w1_strong_pullup ) ) ;
if ( strong_pullup ) {
pr_info ( " %s: Measure with strong_pullup is not supported. \n " , __func__ ) ;
return - EINVAL ;
}
memset ( info - > rom , 0 , sizeof ( info - > rom ) ) ;
/* prevent the slave from going away in sleep */
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto dec_refcnt ;
}
while ( max_trying - - & & ret ) { /* ret should be 0 */
info - > verdict = 0 ;
info - > crc = 0 ;
/* safe version to select slave */
if ( ! reset_select_slave ( sl ) ) {
int j_start , j_end ;
/*no device need pullup */
w1_write_8 ( dev_master , W1_CONVERT_TEMP ) ;
j_start = jiffies ;
ret = w1_poll_completion ( dev_master , W1_POLL_CONVERT_TEMP ) ;
if ( ret ) {
dev_dbg ( & sl - > dev , " %s: Timeout \n " , __func__ ) ;
goto mt_unlock ;
}
j_end = jiffies ;
/* 1.2x increase for variation and changes over temperature range */
* conv_time = jiffies_to_msecs ( j_end - j_start ) * 12 / 10 ;
pr_debug ( " W1 Measure complete, conv_time = %d, HZ=%d. \n " ,
* conv_time , HZ ) ;
if ( * conv_time < = CONV_TIME_MEASURE ) {
ret = - EIO ;
goto mt_unlock ;
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
ret = read_scratchpad ( sl , info ) ;
goto dec_refcnt ;
}
}
mt_unlock :
mutex_unlock ( & dev_master - > bus_mutex ) ;
dec_refcnt :
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
error :
return ret ;
}
2020-05-11 23:37:08 +03:00
static int read_scratchpad ( struct w1_slave * sl , struct therm_info * info )
{
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int ret = - ENODEV ;
info - > verdict = 0 ;
if ( ! sl - > family_data )
goto error ;
memset ( info - > rom , 0 , sizeof ( info - > rom ) ) ;
/* prevent the slave from going away in sleep */
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto dec_refcnt ;
}
while ( max_trying - - & & ret ) { /* ret should be 0 */
/* safe version to select slave */
if ( ! reset_select_slave ( sl ) ) {
u8 nb_bytes_read ;
w1_write_8 ( dev_master , W1_READ_SCRATCHPAD ) ;
nb_bytes_read = w1_read_block ( dev_master , info - > rom , 9 ) ;
if ( nb_bytes_read ! = 9 ) {
dev_warn ( & sl - > dev ,
" w1_read_block(): returned %u instead of 9. \n " ,
nb_bytes_read ) ;
ret = - EIO ;
}
info - > crc = w1_calc_crc8 ( info - > rom , 8 ) ;
if ( info - > rom [ 8 ] = = info - > crc ) {
info - > verdict = 1 ;
ret = 0 ;
} else
ret = - EIO ; /* CRC not checked */
}
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
dec_refcnt :
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
error :
return ret ;
}
static int write_scratchpad ( struct w1_slave * sl , const u8 * data , u8 nb_bytes )
{
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int ret = - ENODEV ;
if ( ! sl - > family_data )
goto error ;
/* prevent the slave from going away in sleep */
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto dec_refcnt ;
}
while ( max_trying - - & & ret ) { /* ret should be 0 */
/* safe version to select slave */
if ( ! reset_select_slave ( sl ) ) {
w1_write_8 ( dev_master , W1_WRITE_SCRATCHPAD ) ;
w1_write_block ( dev_master , data , nb_bytes ) ;
ret = 0 ;
}
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
dec_refcnt :
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
error :
return ret ;
}
2020-05-11 23:37:25 +03:00
static int copy_scratchpad ( struct w1_slave * sl )
2020-05-11 23:35:35 +03:00
{
2020-05-11 23:37:25 +03:00
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int t_write , ret = - ENODEV ;
bool strong_pullup ;
2020-05-11 23:35:35 +03:00
2020-05-11 23:37:25 +03:00
if ( ! sl - > family_data )
2020-05-11 23:35:35 +03:00
goto error ;
2020-05-11 23:37:25 +03:00
t_write = W1_THERM_EEPROM_WRITE_DELAY ;
strong_pullup = ( w1_strong_pullup = = 2 | |
( ! SLAVE_POWERMODE ( sl ) & &
w1_strong_pullup ) ) ;
2020-05-11 23:35:35 +03:00
/* prevent the slave from going away in sleep */
2020-05-11 23:37:25 +03:00
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
2020-05-11 23:35:35 +03:00
2020-05-11 23:37:25 +03:00
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
2020-05-11 23:35:35 +03:00
goto dec_refcnt ;
2020-05-11 23:37:25 +03:00
}
2020-05-11 23:35:35 +03:00
2020-05-11 23:37:25 +03:00
while ( max_trying - - & & ret ) { /* ret should be 0 */
/* safe version to select slave */
2020-05-11 23:36:10 +03:00
if ( ! reset_select_slave ( sl ) ) {
2020-05-11 23:35:35 +03:00
unsigned long sleep_rem ;
2020-05-11 23:37:25 +03:00
/* 10ms strong pullup (or delay) after the convert */
if ( strong_pullup )
w1_next_pullup ( dev_master , t_write ) ;
2020-05-11 23:35:35 +03:00
2020-05-11 23:37:25 +03:00
w1_write_8 ( dev_master , W1_COPY_SCRATCHPAD ) ;
2020-05-11 23:35:35 +03:00
2020-05-11 23:37:25 +03:00
if ( strong_pullup ) {
sleep_rem = msleep_interruptible ( t_write ) ;
2020-05-11 23:35:35 +03:00
if ( sleep_rem ! = 0 ) {
ret = - EINTR ;
goto mt_unlock ;
}
}
2020-05-11 23:37:25 +03:00
ret = 0 ;
2020-05-11 23:35:35 +03:00
}
2020-05-11 23:37:25 +03:00
2020-05-11 23:35:35 +03:00
}
mt_unlock :
2020-05-11 23:37:25 +03:00
mutex_unlock ( & dev_master - > bus_mutex ) ;
2020-05-11 23:35:35 +03:00
dec_refcnt :
2020-05-11 23:37:25 +03:00
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
error :
return ret ;
}
static int recall_eeprom ( struct w1_slave * sl )
{
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int ret = - ENODEV ;
if ( ! sl - > family_data )
goto error ;
/* prevent the slave from going away in sleep */
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto dec_refcnt ;
}
while ( max_trying - - & & ret ) { /* ret should be 0 */
/* safe version to select slave */
if ( ! reset_select_slave ( sl ) ) {
w1_write_8 ( dev_master , W1_RECALL_EEPROM ) ;
2020-09-04 19:00:03 +03:00
ret = w1_poll_completion ( dev_master , W1_POLL_RECALL_EEPROM ) ;
2020-05-11 23:37:25 +03:00
}
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
dec_refcnt :
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
2020-05-11 23:35:35 +03:00
error :
return ret ;
}
2020-05-11 23:36:50 +03:00
static int read_powermode ( struct w1_slave * sl )
{
struct w1_master * dev_master = sl - > master ;
int max_trying = W1_THERM_MAX_TRY ;
int ret = - ENODEV ;
if ( ! sl - > family_data )
goto error ;
/* prevent the slave from going away in sleep */
atomic_inc ( THERM_REFCNT ( sl - > family_data ) ) ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto dec_refcnt ;
}
while ( ( max_trying - - ) & & ( ret < 0 ) ) {
/* safe version to select slave */
if ( ! reset_select_slave ( sl ) ) {
w1_write_8 ( dev_master , W1_READ_PSUPPLY ) ;
/*
* Emit a read time slot and read only one bit ,
* 1 is externally powered ,
* 0 is parasite powered
*/
ret = w1_touch_bit ( dev_master , 1 ) ;
/* ret should be either 1 either 0 */
}
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
dec_refcnt :
atomic_dec ( THERM_REFCNT ( sl - > family_data ) ) ;
error :
return ret ;
}
2020-05-11 23:38:20 +03:00
static int trigger_bulk_read ( struct w1_master * dev_master )
{
struct w1_slave * sl = NULL ; /* used to iterate through slaves */
int max_trying = W1_THERM_MAX_TRY ;
int t_conv = 0 ;
int ret = - ENODEV ;
bool strong_pullup = false ;
/*
* Check whether there are parasite powered device on the bus ,
* and compute duration of conversion for these devices
* so we can apply a strong pullup if required
*/
list_for_each_entry ( sl , & dev_master - > slist , w1_slave_entry ) {
if ( ! sl - > family_data )
goto error ;
if ( bulk_read_support ( sl ) ) {
int t_cur = conversion_time ( sl ) ;
2022-03-10 13:29:29 +03:00
t_conv = max ( t_cur , t_conv ) ;
2020-05-11 23:38:20 +03:00
strong_pullup = strong_pullup | |
( w1_strong_pullup = = 2 | |
( ! SLAVE_POWERMODE ( sl ) & &
w1_strong_pullup ) ) ;
}
}
/*
* t_conv is the max conversion time required on the bus
* If its 0 , no device support the bulk read feature
*/
if ( ! t_conv )
goto error ;
if ( ! bus_mutex_lock ( & dev_master - > bus_mutex ) ) {
ret = - EAGAIN ; /* Didn't acquire the mutex */
goto error ;
}
while ( ( max_trying - - ) & & ( ret < 0 ) ) { /* ret should be either 0 */
if ( ! w1_reset_bus ( dev_master ) ) { /* Just reset the bus */
unsigned long sleep_rem ;
w1_write_8 ( dev_master , W1_SKIP_ROM ) ;
if ( strong_pullup ) /* Apply pullup if required */
w1_next_pullup ( dev_master , t_conv ) ;
w1_write_8 ( dev_master , W1_CONVERT_TEMP ) ;
/* set a flag to instruct that converT pending */
list_for_each_entry ( sl ,
& dev_master - > slist , w1_slave_entry ) {
if ( bulk_read_support ( sl ) )
SLAVE_CONVERT_TRIGGERED ( sl ) = - 1 ;
}
if ( strong_pullup ) { /* some device need pullup */
sleep_rem = msleep_interruptible ( t_conv ) ;
if ( sleep_rem ! = 0 ) {
ret = - EINTR ;
goto mt_unlock ;
}
mutex_unlock ( & dev_master - > bus_mutex ) ;
} else {
mutex_unlock ( & dev_master - > bus_mutex ) ;
sleep_rem = msleep_interruptible ( t_conv ) ;
if ( sleep_rem ! = 0 ) {
ret = - EINTR ;
goto set_flag ;
}
}
ret = 0 ;
goto set_flag ;
}
}
mt_unlock :
mutex_unlock ( & dev_master - > bus_mutex ) ;
set_flag :
/* set a flag to register convsersion is done */
list_for_each_entry ( sl , & dev_master - > slist , w1_slave_entry ) {
if ( bulk_read_support ( sl ) )
SLAVE_CONVERT_TRIGGERED ( sl ) = 1 ;
}
error :
return ret ;
}
2020-05-11 23:35:35 +03:00
/* Sysfs Interface definition */
2017-08-31 02:34:34 +03:00
static ssize_t w1_slave_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
struct therm_info info ;
u8 * family_data = sl - > family_data ;
int ret , i ;
ssize_t c = PAGE_SIZE ;
2020-05-11 23:38:20 +03:00
if ( bulk_read_support ( sl ) ) {
if ( SLAVE_CONVERT_TRIGGERED ( sl ) < 0 ) {
dev_dbg ( device ,
" %s: Conversion in progress, retry later \n " ,
__func__ ) ;
return 0 ;
} else if ( SLAVE_CONVERT_TRIGGERED ( sl ) > 0 ) {
/* A bulk read has been issued, read the device RAM */
ret = read_scratchpad ( sl , & info ) ;
SLAVE_CONVERT_TRIGGERED ( sl ) = 0 ;
} else
ret = convert_t ( sl , & info ) ;
} else
ret = convert_t ( sl , & info ) ;
2020-05-11 23:37:42 +03:00
if ( ret < 0 ) {
dev_dbg ( device ,
" %s: Temperature data may be corrupted. err=%d \n " ,
__func__ , ret ) ;
return 0 ;
}
2017-08-31 02:34:34 +03:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 9 ; + + i )
2017-08-31 02:34:34 +03:00
c - = snprintf ( buf + PAGE_SIZE - c , c , " %02x " , info . rom [ i ] ) ;
2008-10-16 09:04:51 +04:00
c - = snprintf ( buf + PAGE_SIZE - c , c , " : crc=%02x %s \n " ,
2017-08-31 02:34:34 +03:00
info . crc , ( info . verdict ) ? " YES " : " NO " ) ;
2020-05-11 23:37:42 +03:00
2017-08-31 02:34:34 +03:00
if ( info . verdict )
memcpy ( family_data , info . rom , sizeof ( info . rom ) ) ;
2005-04-17 02:20:36 +04:00
else
2020-05-11 23:37:42 +03:00
dev_warn ( device , " %s:Read failed CRC check \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 9 ; + + i )
2014-01-16 08:29:24 +04:00
c - = snprintf ( buf + PAGE_SIZE - c , c , " %02x " ,
2015-05-09 03:51:50 +03:00
( ( u8 * ) family_data ) [ i ] ) ;
2005-12-06 13:38:28 +03:00
2008-10-16 09:04:51 +04:00
c - = snprintf ( buf + PAGE_SIZE - c , c , " t=%d \n " ,
2020-05-11 23:37:42 +03:00
temperature_from_RAM ( sl , info . rom ) ) ;
2015-05-09 03:51:50 +03:00
ret = PAGE_SIZE - c ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2020-05-11 23:35:35 +03:00
static ssize_t w1_slave_store ( struct device * device ,
struct device_attribute * attr , const char * buf ,
size_t size )
{
2020-05-11 23:37:08 +03:00
int val , ret = 0 ;
2020-05-11 23:35:35 +03:00
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
2020-05-11 23:37:08 +03:00
ret = kstrtoint ( buf , 10 , & val ) ; /* converting user entry to int */
2020-05-11 23:35:35 +03:00
2020-05-11 23:37:08 +03:00
if ( ret ) { /* conversion error */
dev_info ( device ,
" %s: conversion error. err= %d \n " , __func__ , ret ) ;
return size ; /* return size to avoid call back again */
}
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return size ; /* No device family */
2020-05-11 23:35:35 +03:00
}
2020-05-11 23:37:08 +03:00
if ( val = = 0 ) /* val=0 : trigger a EEPROM save */
2020-05-11 23:37:25 +03:00
ret = copy_scratchpad ( sl ) ;
2020-05-11 23:37:08 +03:00
else {
if ( SLAVE_SPECIFIC_FUNC ( sl ) - > set_resolution )
ret = SLAVE_SPECIFIC_FUNC ( sl ) - > set_resolution ( sl , val ) ;
}
if ( ret ) {
2020-09-04 19:00:03 +03:00
dev_warn ( device , " %s: Set resolution - error %d \n " , __func__ , ret ) ;
/* Propagate error to userspace */
return ret ;
}
SLAVE_RESOLUTION ( sl ) = val ;
/* Reset the conversion time to default - it depends on resolution */
SLAVE_CONV_TIME_OVERRIDE ( sl ) = CONV_TIME_DEFAULT ;
2020-05-11 23:37:08 +03:00
return size ; /* always return size to avoid infinite calling */
2020-05-11 23:35:35 +03:00
}
2020-05-11 23:37:42 +03:00
static ssize_t temperature_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
struct therm_info info ;
int ret = 0 ;
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return 0 ; /* No device family */
}
2020-05-11 23:38:20 +03:00
if ( bulk_read_support ( sl ) ) {
if ( SLAVE_CONVERT_TRIGGERED ( sl ) < 0 ) {
dev_dbg ( device ,
" %s: Conversion in progress, retry later \n " ,
__func__ ) ;
return 0 ;
} else if ( SLAVE_CONVERT_TRIGGERED ( sl ) > 0 ) {
/* A bulk read has been issued, read the device RAM */
ret = read_scratchpad ( sl , & info ) ;
SLAVE_CONVERT_TRIGGERED ( sl ) = 0 ;
} else
ret = convert_t ( sl , & info ) ;
} else
ret = convert_t ( sl , & info ) ;
2020-05-11 23:37:42 +03:00
if ( ret < 0 ) {
dev_dbg ( device ,
" %s: Temperature data may be corrupted. err=%d \n " ,
__func__ , ret ) ;
return 0 ;
}
return sprintf ( buf , " %d \n " , temperature_from_RAM ( sl , info . rom ) ) ;
}
2020-05-11 23:36:50 +03:00
static ssize_t ext_power_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
if ( ! sl - > family_data ) {
dev_info ( device ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return 0 ; /* No device family */
}
/* Getting the power mode of the device {external, parasite} */
SLAVE_POWERMODE ( sl ) = read_powermode ( sl ) ;
if ( SLAVE_POWERMODE ( sl ) < 0 ) {
dev_dbg ( device ,
" %s: Power_mode may be corrupted. err=%d \n " ,
__func__ , SLAVE_POWERMODE ( sl ) ) ;
}
return sprintf ( buf , " %d \n " , SLAVE_POWERMODE ( sl ) ) ;
}
2020-05-11 23:37:08 +03:00
static ssize_t resolution_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return 0 ; /* No device family */
}
/* get the correct function depending on the device */
SLAVE_RESOLUTION ( sl ) = SLAVE_SPECIFIC_FUNC ( sl ) - > get_resolution ( sl ) ;
if ( SLAVE_RESOLUTION ( sl ) < 0 ) {
dev_dbg ( device ,
" %s: Resolution may be corrupted. err=%d \n " ,
__func__ , SLAVE_RESOLUTION ( sl ) ) ;
}
return sprintf ( buf , " %d \n " , SLAVE_RESOLUTION ( sl ) ) ;
}
static ssize_t resolution_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
int val ;
int ret = 0 ;
ret = kstrtoint ( buf , 10 , & val ) ; /* converting user entry to int */
if ( ret ) { /* conversion error */
dev_info ( device ,
" %s: conversion error. err= %d \n " , __func__ , ret ) ;
return size ; /* return size to avoid call back again */
}
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return size ; /* No device family */
}
/*
* Don ' t deal with the val enterd by user ,
* only device knows what is correct or not
*/
/* get the correct function depending on the device */
ret = SLAVE_SPECIFIC_FUNC ( sl ) - > set_resolution ( sl , val ) ;
2020-09-04 19:00:03 +03:00
if ( ret )
return ret ;
SLAVE_RESOLUTION ( sl ) = val ;
/* Reset the conversion time to default because it depends on resolution */
SLAVE_CONV_TIME_OVERRIDE ( sl ) = CONV_TIME_DEFAULT ;
2020-05-11 23:37:08 +03:00
return size ;
}
2020-11-12 09:49:31 +03:00
static ssize_t eeprom_cmd_store ( struct device * device ,
2020-05-11 23:37:25 +03:00
struct device_attribute * attr , const char * buf , size_t size )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
int ret = - EINVAL ; /* Invalid argument */
if ( size = = sizeof ( EEPROM_CMD_WRITE ) ) {
if ( ! strncmp ( buf , EEPROM_CMD_WRITE , sizeof ( EEPROM_CMD_WRITE ) - 1 ) )
ret = copy_scratchpad ( sl ) ;
} else if ( size = = sizeof ( EEPROM_CMD_READ ) ) {
if ( ! strncmp ( buf , EEPROM_CMD_READ , sizeof ( EEPROM_CMD_READ ) - 1 ) )
ret = recall_eeprom ( sl ) ;
}
if ( ret )
dev_info ( device , " %s: error in process %d \n " , __func__ , ret ) ;
return size ;
}
2020-05-11 23:38:01 +03:00
static ssize_t alarms_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
2020-05-19 18:45:53 +03:00
int ret ;
2020-05-11 23:38:01 +03:00
s8 th = 0 , tl = 0 ;
struct therm_info scratchpad ;
ret = read_scratchpad ( sl , & scratchpad ) ;
if ( ! ret ) {
th = scratchpad . rom [ 2 ] ; /* TH is byte 2 */
tl = scratchpad . rom [ 3 ] ; /* TL is byte 3 */
} else {
dev_info ( device ,
" %s: error reading alarms register %d \n " ,
__func__ , ret ) ;
}
return sprintf ( buf , " %hd %hd \n " , tl , th ) ;
}
static ssize_t alarms_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
struct therm_info info ;
u8 new_config_register [ 3 ] ; /* array of data to be written */
2020-05-19 18:45:53 +03:00
int temp , ret ;
2020-05-11 23:38:01 +03:00
char * token = NULL ;
2021-12-21 12:15:59 +03:00
s8 tl , th ; /* 1 byte per value + temp ring order */
2020-05-20 15:00:19 +03:00
char * p_args , * orig ;
2020-05-11 23:38:01 +03:00
2020-05-20 15:00:19 +03:00
p_args = orig = kmalloc ( size , GFP_KERNEL ) ;
2020-05-11 23:38:01 +03:00
/* Safe string copys as buf is const */
if ( ! p_args ) {
dev_warn ( device ,
" %s: error unable to allocate memory %d \n " ,
__func__ , - ENOMEM ) ;
return size ;
}
strcpy ( p_args , buf ) ;
/* Split string using space char */
token = strsep ( & p_args , " " ) ;
if ( ! token ) {
dev_info ( device ,
" %s: error parsing args %d \n " , __func__ , - EINVAL ) ;
goto free_m ;
}
/* Convert 1st entry to int */
ret = kstrtoint ( token , 10 , & temp ) ;
if ( ret ) {
dev_info ( device ,
" %s: error parsing args %d \n " , __func__ , ret ) ;
goto free_m ;
}
tl = int_to_short ( temp ) ;
/* Split string using space char */
token = strsep ( & p_args , " " ) ;
if ( ! token ) {
dev_info ( device ,
" %s: error parsing args %d \n " , __func__ , - EINVAL ) ;
goto free_m ;
}
/* Convert 2nd entry to int */
ret = kstrtoint ( token , 10 , & temp ) ;
if ( ret ) {
dev_info ( device ,
" %s: error parsing args %d \n " , __func__ , ret ) ;
goto free_m ;
}
/* Prepare to cast to short by eliminating out of range values */
th = int_to_short ( temp ) ;
/* Reorder if required th and tl */
2021-12-21 12:15:59 +03:00
if ( tl > th )
swap ( tl , th ) ;
2020-05-11 23:38:01 +03:00
/*
* Read the scratchpad to change only the required bits
* ( th : byte 2 - tl : byte 3 )
*/
ret = read_scratchpad ( sl , & info ) ;
if ( ! ret ) {
new_config_register [ 0 ] = th ; /* Byte 2 */
new_config_register [ 1 ] = tl ; /* Byte 3 */
new_config_register [ 2 ] = info . rom [ 4 ] ; /* Byte 4 */
} else {
dev_info ( device ,
" %s: error reading from the slave device %d \n " ,
__func__ , ret ) ;
goto free_m ;
}
/* Write data in the device RAM */
if ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) {
dev_info ( device ,
" %s: Device not supported by the driver %d \n " ,
__func__ , - ENODEV ) ;
goto free_m ;
}
ret = SLAVE_SPECIFIC_FUNC ( sl ) - > write_data ( sl , new_config_register ) ;
if ( ret )
dev_info ( device ,
" %s: error writing to the slave device %d \n " ,
__func__ , ret ) ;
free_m :
/* free allocated memory */
2020-05-20 15:00:19 +03:00
kfree ( orig ) ;
2020-05-11 23:38:01 +03:00
return size ;
}
2020-05-11 23:38:20 +03:00
static ssize_t therm_bulk_read_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size )
{
struct w1_master * dev_master = dev_to_w1_master ( device ) ;
int ret = - EINVAL ; /* Invalid argument */
if ( size = = sizeof ( BULK_TRIGGER_CMD ) )
if ( ! strncmp ( buf , BULK_TRIGGER_CMD ,
sizeof ( BULK_TRIGGER_CMD ) - 1 ) )
ret = trigger_bulk_read ( dev_master ) ;
if ( ret )
dev_info ( device ,
" %s: unable to trigger a bulk read on the bus. err=%d \n " ,
__func__ , ret ) ;
return size ;
}
static ssize_t therm_bulk_read_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_master * dev_master = dev_to_w1_master ( device ) ;
struct w1_slave * sl = NULL ;
int ret = 0 ;
list_for_each_entry ( sl , & dev_master - > slist , w1_slave_entry ) {
if ( sl - > family_data ) {
if ( bulk_read_support ( sl ) ) {
if ( SLAVE_CONVERT_TRIGGERED ( sl ) = = - 1 ) {
ret = - 1 ;
goto show_result ;
}
if ( SLAVE_CONVERT_TRIGGERED ( sl ) = = 1 )
/* continue to check other slaves */
ret = 1 ;
}
}
}
show_result :
return sprintf ( buf , " %d \n " , ret ) ;
}
2020-09-04 19:00:03 +03:00
static ssize_t conv_time_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device ,
" %s: Device is not supported by the driver \n " , __func__ ) ;
return 0 ; /* No device family */
}
return sprintf ( buf , " %d \n " , conversion_time ( sl ) ) ;
}
static ssize_t conv_time_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size )
{
int val , ret = 0 ;
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
if ( kstrtoint ( buf , 10 , & val ) ) /* converting user entry to int */
return - EINVAL ;
if ( check_family_data ( sl ) )
return - ENODEV ;
if ( val ! = CONV_TIME_MEASURE ) {
if ( val > = CONV_TIME_DEFAULT )
SLAVE_CONV_TIME_OVERRIDE ( sl ) = val ;
else
return - EINVAL ;
} else {
int conv_time ;
ret = conv_time_measure ( sl , & conv_time ) ;
if ( ret )
return - EIO ;
SLAVE_CONV_TIME_OVERRIDE ( sl ) = conv_time ;
}
return size ;
}
static ssize_t features_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device ,
" %s: Device not supported by the driver \n " , __func__ ) ;
return 0 ; /* No device family */
}
return sprintf ( buf , " %u \n " , SLAVE_FEATURES ( sl ) ) ;
}
static ssize_t features_store ( struct device * device ,
struct device_attribute * attr , const char * buf , size_t size )
{
int val , ret = 0 ;
bool strong_pullup ;
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
ret = kstrtouint ( buf , 10 , & val ) ; /* converting user entry to int */
if ( ret )
return - EINVAL ; /* invalid number */
if ( ( ! sl - > family_data ) | | ( ! SLAVE_SPECIFIC_FUNC ( sl ) ) ) {
dev_info ( device , " %s: Device not supported by the driver \n " , __func__ ) ;
return - ENODEV ;
}
if ( ( val & W1_THERM_FEATURES_MASK ) ! = val )
return - EINVAL ;
SLAVE_FEATURES ( sl ) = val ;
strong_pullup = ( w1_strong_pullup = = 2 | |
( ! SLAVE_POWERMODE ( sl ) & &
w1_strong_pullup ) ) ;
if ( strong_pullup & & SLAVE_FEATURES ( sl ) & W1_THERM_POLL_COMPLETION ) {
dev_warn ( & sl - > dev ,
" %s: W1_THERM_POLL_COMPLETION disabled in parasite power mode. \n " ,
__func__ ) ;
SLAVE_FEATURES ( sl ) & = ~ W1_THERM_POLL_COMPLETION ;
}
return size ;
}
2017-08-31 02:34:35 +03:00
# if IS_REACHABLE(CONFIG_HWMON)
static int w1_read_temp ( struct device * device , u32 attr , int channel ,
long * val )
{
struct w1_slave * sl = dev_get_drvdata ( device ) ;
struct therm_info info ;
int ret ;
switch ( attr ) {
case hwmon_temp_input :
2020-05-11 23:37:42 +03:00
ret = convert_t ( sl , & info ) ;
2017-08-31 02:34:35 +03:00
if ( ret )
return ret ;
if ( ! info . verdict ) {
ret = - EIO ;
return ret ;
}
2020-05-11 23:37:42 +03:00
* val = temperature_from_RAM ( sl , info . rom ) ;
2017-08-31 02:34:35 +03:00
ret = 0 ;
break ;
default :
ret = - EOPNOTSUPP ;
break ;
}
return ret ;
}
# endif
2015-04-28 14:44:17 +03:00
# define W1_42_CHAIN 0x99
# define W1_42_CHAIN_OFF 0x3C
# define W1_42_CHAIN_OFF_INV 0xC3
# define W1_42_CHAIN_ON 0x5A
# define W1_42_CHAIN_ON_INV 0xA5
# define W1_42_CHAIN_DONE 0x96
# define W1_42_CHAIN_DONE_INV 0x69
# define W1_42_COND_READ 0x0F
# define W1_42_SUCCESS_CONFIRM_BYTE 0xAA
# define W1_42_FINISHED_BYTE 0xFF
static ssize_t w1_seq_show ( struct device * device ,
struct device_attribute * attr , char * buf )
{
struct w1_slave * sl = dev_to_w1_slave ( device ) ;
ssize_t c = PAGE_SIZE ;
int i ;
u8 ack ;
u64 rn ;
struct w1_reg_num * reg_num ;
int seq = 0 ;
2015-06-04 12:04:12 +03:00
mutex_lock ( & sl - > master - > bus_mutex ) ;
2015-04-28 14:44:17 +03:00
/* Place all devices in CHAIN state */
if ( w1_reset_bus ( sl - > master ) )
goto error ;
w1_write_8 ( sl - > master , W1_SKIP_ROM ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN_ON ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN_ON_INV ) ;
msleep ( sl - > master - > pullup_duration ) ;
/* check for acknowledgment */
ack = w1_read_8 ( sl - > master ) ;
if ( ack ! = W1_42_SUCCESS_CONFIRM_BYTE )
goto error ;
2020-05-11 23:35:35 +03:00
/* In case the bus fails to send 0xFF, limit */
2015-04-28 14:44:17 +03:00
for ( i = 0 ; i < = 64 ; i + + ) {
if ( w1_reset_bus ( sl - > master ) )
goto error ;
w1_write_8 ( sl - > master , W1_42_COND_READ ) ;
2021-05-18 08:04:15 +03:00
w1_read_block ( sl - > master , ( u8 * ) & rn , 8 ) ;
2015-04-28 14:44:17 +03:00
reg_num = ( struct w1_reg_num * ) & rn ;
2015-06-01 12:55:37 +03:00
if ( reg_num - > family = = W1_42_FINISHED_BYTE )
2015-04-28 14:44:17 +03:00
break ;
if ( sl - > reg_num . id = = reg_num - > id )
seq = i ;
2022-02-23 14:35:55 +03:00
if ( w1_reset_bus ( sl - > master ) )
goto error ;
/* Put the device into chain DONE state */
w1_write_8 ( sl - > master , W1_MATCH_ROM ) ;
w1_write_block ( sl - > master , ( u8 * ) & rn , 8 ) ;
2015-04-28 14:44:17 +03:00
w1_write_8 ( sl - > master , W1_42_CHAIN ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN_DONE ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN_DONE_INV ) ;
/* check for acknowledgment */
ack = w1_read_8 ( sl - > master ) ;
if ( ack ! = W1_42_SUCCESS_CONFIRM_BYTE )
goto error ;
}
/* Exit from CHAIN state */
if ( w1_reset_bus ( sl - > master ) )
goto error ;
w1_write_8 ( sl - > master , W1_SKIP_ROM ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN_OFF ) ;
w1_write_8 ( sl - > master , W1_42_CHAIN_OFF_INV ) ;
/* check for acknowledgment */
ack = w1_read_8 ( sl - > master ) ;
if ( ack ! = W1_42_SUCCESS_CONFIRM_BYTE )
goto error ;
2015-06-04 12:04:12 +03:00
mutex_unlock ( & sl - > master - > bus_mutex ) ;
2015-04-28 14:44:17 +03:00
c - = snprintf ( buf + PAGE_SIZE - c , c , " %d \n " , seq ) ;
return PAGE_SIZE - c ;
error :
mutex_unlock ( & sl - > master - > bus_mutex ) ;
return - EIO ;
}
2005-04-17 02:20:36 +04:00
static int __init w1_therm_init ( void )
{
2005-04-17 22:58:14 +04:00
int err , i ;
2007-02-12 11:52:05 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( w1_therm_families ) ; + + i ) {
2005-04-17 22:58:14 +04:00
err = w1_register_family ( w1_therm_families [ i ] . f ) ;
if ( err )
w1_therm_families [ i ] . broken = 1 ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void __exit w1_therm_fini ( void )
{
2005-04-17 22:58:14 +04:00
int i ;
2007-02-12 11:52:05 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( w1_therm_families ) ; + + i )
2005-04-17 22:58:14 +04:00
if ( ! w1_therm_families [ i ] . broken )
w1_unregister_family ( w1_therm_families [ i ] . f ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( w1_therm_init ) ;
module_exit ( w1_therm_fini ) ;
2017-05-16 23:02:12 +03:00
MODULE_AUTHOR ( " Evgeniy Polyakov <zbr@ioremap.net> " ) ;
MODULE_DESCRIPTION ( " Driver for 1-wire Dallas network protocol, temperature family. " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " w1-family- " __stringify ( W1_THERM_DS18S20 ) ) ;
MODULE_ALIAS ( " w1-family- " __stringify ( W1_THERM_DS1822 ) ) ;
MODULE_ALIAS ( " w1-family- " __stringify ( W1_THERM_DS18B20 ) ) ;
MODULE_ALIAS ( " w1-family- " __stringify ( W1_THERM_DS1825 ) ) ;
MODULE_ALIAS ( " w1-family- " __stringify ( W1_THERM_DS28EA00 ) ) ;