Hi all,
i've attached a patch for the sensors-detect script that will
detect the SMSC EMC1023 smbus Thermal IC.
also attached is a driver, please let me know if i did anything wrong in
it or did not give anyone proper credit,
I was the tmp401 driver for a framework.
Regards,
Anish
# For building for the current running version of Linux
TARGET := $(shell uname -r)
# Or specific version
#TARGET := 2.6.33.5
#TARGET := 2.6.32.15
KERNEL_MODULES := /lib/modules/$(TARGET)
KERNEL_BUILD := $(KERNEL_MODULES)/build
#DRIVER := pc87427
DRIVER := emc1023
# Directory below /lib/modules/$(TARGET)/kernel into which to install
# the module:
MOD_SUBDIR = drivers/hwmon
obj-m := $(DRIVER).o
EXTRA_CFLAGS += -DDEBUG
.PHONY: all install modules modules_install clean
all: modules
# Targets for running make directly in the external module directory:
modules clean:
@$(MAKE) -C $(KERNEL_BUILD) M=$(CURDIR) $@
install: modules_install
modules_install:
test -d $(KERNEL_MODULES)/kernel/$(MOD_SUBDIR) || mkdir $(KERNEL_MODULES)/kernel/$(MOD_SUBDIR)
cp $(DRIVER).ko $(KERNEL_MODULES)/kernel/$(MOD_SUBDIR)
depmod -a -F $(KERNEL_BUILD)/System.map $(TARGET)
*** sensors-detect Fri Jan 14 11:11:56 2011
--- sensors-detect-new Sat Jan 15 03:01:01 2011
***************
*** 1205,1210 ****
--- 1205,1215 ----
i2c_addrs => [0x18, 0x2a, 0x4c, 0x4d],
i2c_detect => sub { emc1403_detect(@_, 3); },
}, {
+ name => "SMSC_EMC1023",
+ driver => "emc1023",
+ i2c_addrs => [0x48,0x49,0x4c,0x4d],
+ i2c_detect => sub { emc1023_detect(@_); },
+ }, {
name => "ST STTS424",
driver => "jc42",
i2c_addrs => [0x18..0x1f],
***************
*** 5387,5392 ****
--- 5392,5418 ----
return 6;
}
+ # Chip to detect:
+ # Registers used:
+ # 0xed: Device ID register
+ # 0xfe: Vendor ID register
+ # 0xff: Revision register
+ sub emc1023_detect
+ {
+ my ($file, $addr, $chip) = @_;
+ my $dev_id = i2c_smbus_read_byte_data($file, 0xed);
+ my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
+ my $rev = i2c_smbus_read_byte_data($file, 0xff);
+
+ return unless $man_id == 0x5d; # SMSC
+
+ return unless ($dev_id == 0x0c) || ($dev_id == 0x0d) || ($dev_id == 0x08) || ($dev_id == 0x09) ;
+ return unless $rev == 0x01;
+
+ return 9;
+ }
+
+
# This checks for non-FFFF values for temperature, voltage, and current.
# The address (0x0b) is specified by the SMBus standard so it's likely
# that this really is a smart battery.
/* emc1023.c
*
* Copyright (C) 2011 Anish K. Patel <anishs.online.junk@xxxxxxxxx>
* based heavily on tmp401.c
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* Driver for the SMSC EMC1023 SMBUS temperature sensor IC.
* examples taken from datasheet
*
* legacy format (LF)
* registers for temperature store them in an 11bit value,
* 2's complement form
* the high byte holds the sign and the whole number part
* the low byte holds the fraction in the upper 3 bits.
* goes from -63.125 to 127.875
* bit 7 - 0.500
* bit 6 - 0.250
* bit 5 - 0.125
* temp data junk bits hex
* ie Diode fault = 10000000000|00000 400
* -63 = 11000001000|00000 608
* -63.875 = 11000000001|00000 601
* 0 = 00000000000|00000 000
* 1 = 00000001000|00000 008
* 127 = 01111111000|00000 3F8
* 127.875 = 01111111111|00000 3FF
*
* for the extend format (EF) add 64d to read the data
*
*
* todo - clean up the fraction part, implement EF temp
* *possibly make EF reporting default with module flag
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x48,0x49,0x4c,0x4d, I2C_CLIENT_END };
enum chips { emc1023_1 , emc1023_2, emc1023_3, emc1023_4 };
/*
* The EMC1023 registers, note some registers have different addresses for
* reading and writing
* device id - seems to be the 8,9,c,d corresponding to address of IC
* ie devid 0x08 ic addr 0x48, etc
*/
#define EMC1023_ONE_SHOT_CMD 0x0F
#define EMC1023_RMT1_IF_Rw 0x27
#define EMC1023_RMT2_IF_RW 0x28
#define EMC1023_STATUS 0x02
#define EMC1023_CONFIG_READ 0x03
#define EMC1023_CONFIG_WRITE 0x09
#define EMC1023_DEVICE_ID_REG 0xED
#define EMC1023_MANUFACTURER_ID_REG 0xFE
#define EMC1023_PRODUCT_ID_REG 0xFF
#define EMC1023_MANUFACTURER_ID 0x5D
#define EMC1023_DEVICE_ID_1 0x0C
#define EMC1023_DEVICE_ID_2 0x0D
#define EMC1023_DEVICE_ID_3 0x08
#define EMC1023_DEVICE_ID_4 0x09
static const u8 EMC1023_DEVICE_ID[] = { 0x08, 0x09,
0x0c, 0x0d };
static const u8 EMC1023_LF_TEMP_HIGH_BYTE[3] = { 0x00, 0x01, 0xF8 };
static const u8 EMC1023_LF_TEMP_LOW_BYTE[3] = { 0x23, 0x10, 0xF9 };
static const u8 EMC1023_EF_TEMP_HIGH_BYTE[2] = { 0xFA, 0XFC };
static const u8 EMC1023_EF_TEMP_LOW_BYTE[2] = { 0xFB, 0XFD };
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id emc1023_id[] = {
{ "emc1023_1", emc1023_1 },
{ "emc1023_2", emc1023_2 },
{ "emc1023_3", emc1023_3 },
{ "emc1023_4", emc1023_4 },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc1023_id);
/*
* Client data (each client gets its own)
*/
struct threebit{
u8 info:3;
};
struct emc1023_data {
struct device *hwmon_dev;
struct mutex update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
enum chips kind;
/* register values */
u8 status;
u8 config;
u16 temp;
u8 temp_high[3];
u8 temp_low[3];
};
/*
* Sysfs attr show / store functions
*/
static int emc1023_register_to_temp(u8 reg_high,u8 reg_low)
{
int temphigh = reg_high;
//since we only need the top 3bits from reg_low shift over 5
//and then do the magic below to pop temp low with correct data
int templow = (reg_low >> 5);
if ( temphigh > 128 )
temphigh = ((temphigh % 128 ) - 128)*1000;
else
temphigh *= 1000;
//get fraction part and populate temp low with info (ugly)
templow =(((templow&4)>>2)*500 )+ (((templow&2)>>1)*250 )+ ((templow&1)*125);
return (temphigh+templow);
}
static struct emc1023_data *emc1023_update_device_reg16(
struct i2c_client *client, struct emc1023_data *data)
{
int i;
for (i=0; i < 3; i++) {
data->temp_high[i] = i2c_smbus_read_byte_data(client,
EMC1023_LF_TEMP_HIGH_BYTE[i]);
data->temp_low[i] = i2c_smbus_read_byte_data(client,
EMC1023_LF_TEMP_LOW_BYTE[i]);
}
return data;
}
static struct emc1023_data *emc1023_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct emc1023_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
data->status = i2c_smbus_read_byte_data(client, EMC1023_STATUS);
data->config = i2c_smbus_read_byte_data(client,
EMC1023_CONFIG_READ);
emc1023_update_device_reg16(client, data);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static ssize_t show_temp_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct emc1023_data *data = emc1023_update_device(dev);
return sprintf(buf, "%d\n",
emc1023_register_to_temp(data->temp_high[index], data->temp_low[index]));
}
/*static ssize_t show_status(struct device *dev,
struct device_attribute *devattr, char *buf)
{
//int mask = to_sensor_dev_attr(devattr)->index;
struct emc1023_data *data = emc1023_update_device(dev);
if ( (data->status >> 7) == 1 )
return sprintf(buf, "ready\n");
else
return sprintf(buf, "not ready\n");
}*/
static struct sensor_device_attribute emc1023_attr[] = {
SENSOR_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0),
SENSOR_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1),
SENSOR_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2),
};
/*
* Begin non sysfs callback code (aka Real code)
*/
static void emc1023_init_client(struct i2c_client *client)
{
int config;
/* Start conversions (disable shutdown if necessary) */
config = i2c_smbus_read_byte_data(client, EMC1023_CONFIG_READ);
if (config < 0) {
dev_warn(&client->dev, "Initialization failed!\n");
return;
}
}
static int emc1023_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
//int i=0;
struct i2c_adapter *adapter = client->adapter;
u8 reg;
enum chips kind;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* Detect and identify the chip */
reg = i2c_smbus_read_byte_data(client, EMC1023_MANUFACTURER_ID_REG);
if (reg != EMC1023_MANUFACTURER_ID)
return -ENODEV;
reg = i2c_smbus_read_byte_data(client, EMC1023_DEVICE_ID_REG);
//tried to cycle through array to match id.
/*for(i=0;i<4;i++)
if( reg == EMC1023_DEVICE_ID[i] )
{
kind = i;
break;
}*/
switch (reg) {
case EMC1023_DEVICE_ID_1:
kind = emc1023_1;
break;
case EMC1023_DEVICE_ID_2:
kind = emc1023_2;
break;
case EMC1023_DEVICE_ID_3:
kind = emc1023_3;
break;
case EMC1023_DEVICE_ID_4:
kind = emc1023_4;
break;
default:
return -ENODEV;
}
strlcpy(info->type, emc1023_id[kind].name, I2C_NAME_SIZE);
return 0;
}
static int emc1023_remove(struct i2c_client *client)
{
struct emc1023_data *data = i2c_get_clientdata(client);
int i;
if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev);
for (i = 0; i < ARRAY_SIZE(emc1023_attr); i++)
device_remove_file(&client->dev, &emc1023_attr[i].dev_attr);
kfree(data);
return 0;
}
static int emc1023_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i, err = 0;
struct emc1023_data *data;
const char *names[] = { "EMC1023_1", "EMC1023_2", "EMC1023_3","EMC1023_4" };
data = kzalloc(sizeof(struct emc1023_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->kind = id->driver_data;
/* Initialize the EMC1023 chip */
emc1023_init_client(client);
/* Register sysfs hooks */
for (i = 0; i < ARRAY_SIZE(emc1023_attr); i++) {
err = device_create_file(&client->dev,
&emc1023_attr[i].dev_attr);
if (err)
goto exit_remove;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
data->hwmon_dev = NULL;
goto exit_remove;
}
dev_info(&client->dev, "Detected emc1023 %s chip\n", names[data->kind]);
return 0;
exit_remove:
emc1023_remove(client); /* will also free data for us */
return err;
}
static struct i2c_driver emc1023_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "emc1023",
},
.probe = emc1023_probe,
.remove = emc1023_remove,
.id_table = emc1023_id,
.detect = emc1023_detect,
.address_list = normal_i2c,
};
static int __init emc1023_init(void)
{
return i2c_add_driver(&emc1023_driver);
}
static void __exit emc1023_exit(void)
{
i2c_del_driver(&emc1023_driver);
}
MODULE_AUTHOR("Anish K Patel <anishp@xxxxxxxxxxx!>");
MODULE_DESCRIPTION("SMSC emc1023 temperature sensor driver");
MODULE_LICENSE("GPL");
module_init(emc1023_init);
module_exit(emc1023_exit);
_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors