All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anish Patel <anish.mailing.list@gmail.com>
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] support for emc1023
Date: Sat, 15 Jan 2011 18:33:52 +0000	[thread overview]
Message-ID: <4D31E890.7030004@gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 292 bytes --]

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


[-- Attachment #2: Makefile --]
[-- Type: text/plain, Size: 874 bytes --]

# 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)

[-- Attachment #3: sensors-detect.patch --]
[-- Type: text/plain, Size: 1295 bytes --]

*** 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.

[-- Attachment #4: emc1023.c --]
[-- Type: text/x-c, Size: 9571 bytes --]

/* emc1023.c
 *
 * Copyright (C) 2011 Anish K. Patel <anishs.online.junk@gmail.com>
 * 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@win-ent.com!>");
MODULE_DESCRIPTION("SMSC emc1023 temperature sensor driver");
MODULE_LICENSE("GPL");

module_init(emc1023_init);
module_exit(emc1023_exit);

[-- Attachment #5: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

             reply	other threads:[~2011-01-15 18:33 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-01-15 18:33 Anish Patel [this message]
2011-01-16  7:22 ` [lm-sensors] support for emc1023 Guenter Roeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4D31E890.7030004@gmail.com \
    --to=anish.mailing.list@gmail.com \
    --cc=lm-sensors@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.