new sensors driver: SMSC LPC47B397-NC
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
Hi Greg:
This patch adds support for the SMSC LPC47B397-NC sensor chip. It was sponsored by In-Store Broadcasting Network.
Index: linux-2.6.10-mmh/Documentation/i2c/chips/smsc47b397.txt
=================================--- linux-2.6.10-mmh.orig/Documentation/i2c/chips/smsc47b397.txt 2004-12-29 05:08:59.698570592 -0500
+++ linux-2.6.10-mmh/Documentation/i2c/chips/smsc47b397.txt 2004-12-26 20:48:26.000000000 -0500
@@ -0,0 +1,128 @@
+November 23, 2004
+
+The following specification describes the SMSC LPC47B397-NC sensor chip
+(for which there is no public datasheet available). This document was
+provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected
+by Mark M. Hoffman <[email protected]>.
+
+* * * * *
+
+Methods for detecting the HP SIO and reading the thermal data on a dc7100.
+
+The thermal information on the dc7100 is contained in the SIO Hardware Monitor (HWM). The information is accessed through an index/data pair. The index/data pair is located at the HWM Base Address + 0 and the HWM Base Address + 1. The HWM Base address can be obtained from Logical Device 8, registers 0x60 (MSB) and 0x61 (LSB). Currently we are using 0x480 for the HWM Base Address and 0x480 and 0x481 for the index/data pair. +
+Reading temperature information.
+The temperature information is located in the following registers:
+Temp1 0x25 (Currently, this reflects the CPU temp on all systems).
+Temp2 0x26
+Temp3 0x27
+Temp4 0x80
+
+Programming Example
+The following is an example of how to read the HWM temperature registers:
+MOV DX,480H
+MOV AX,25H
+OUT DX,AL
+MOV DX,481H
+IN AL,DX
+
+AL contains the data in hex, the temperature in Celsius is the decimal equivalent.
+
+Ex: If AL contains 0x2A, the temperature is 42 degrees C.
+
+Reading tach information.
+The fan speed information is located in the following registers:
+ LSB MSB
+Tach1 0x28 0x29 (Currently, this reflects the CPU fan speed on all systems).
+Tach2 0x2A 0x2B
+Tach3 0x2C 0x2D
+Tach4 0x2E 0x2F
+
+Important!!!
+Reading the tach LSB locks the tach MSB.
+The LSB Must be read first.
+
+How to convert the tach reading to RPM.
+The tach reading (TCount) is given by: (Tach MSB * 256) + (Tach LSB)
+The SIO counts the number of 90kHz (11.111us) pulses per revolution.
+RPM = 60/(TCount * 11.111us)
+
+Example:
+Reg 0x28 = 0x9B
+Reg 0x29 = 0x08
+
+TCount = 0x89B = 2203
+
+RPM = 60 / (2203 * 11.11111 E-6) = 2451 RPM
+
+Obtaining the SIO version.
+
+CONFIGURATION SEQUENCE
+To program the configuration registers, the following sequence must be followed:
+1. Enter Configuration Mode
+2. Configure the Configuration Registers
+3. Exit Configuration Mode.
+
+Enter Configuration Mode
+To place the chip into the Configuration State The config key (0x55) is written to the CONFIG PORT (0x2E).
+
+Configuration Mode
+In configuration mode, the INDEX PORT is located at the CONFIG PORT address and the DATA PORT is at INDEX PORT address + 1.
+
+The desired configuration registers are accessed in two steps:
+a. Write the index of the Logical Device Number Configuration Register (i.e., 0x07) to the INDEX PORT and then write the number of the desired logical device to the DATA PORT.
+b. Write the address of the desired configuration register within the logical device to the INDEX PORT and then write or read the configuration register through the DATA PORT.
+Note: If accessing the Global Configuration Registers, step (a) is not required.
+
+Exit Configuration Mode
+To exit the Configuration State the write 0xAA to the CONFIG PORT (0x2E). The chip returns to the RUN State. (This is important).
+
+Programming Example
+The following is an example of how to read the SIO Device ID located at 0x20
+
+; ENTER CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,055H
+OUT DX,AL
+; GLOBAL CONFIGURATION REGISTER
+MOV DX,02EH
+MOV AL,20H
+OUT DX,AL
+; READ THE DATA
+MOV DX,02FH
+IN AL,DX
+; EXIT CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,0AAH
+OUT DX,AL
+
+The registers of interest for identifying the SIO on the dc7100 are Device ID (0x20) and Device Rev (0x21).
+
+The Device ID will read 0X6F
+The Device Rev currently reads 0x01
+
+Obtaining the HWM Base Address.
+The following is an example of how to read the HWM Base Address located in Logical Device 8.
+
+; ENTER CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,055H
+OUT DX,AL
+; CONFIGURE REGISTER CRE0,
+; LOGICAL DEVICE 8
+MOV DX,02EH
+MOV AL,07H
+OUT DX,AL ;Point to LD# Config Reg
+MOV DX,02FH
+MOV AL, 08H
+OUT DX,AL;Point to Logical Device 8
+;
+MOV DX,02EH
+MOV AL,60H
+OUT DX,AL ; Point to HWM Base Addr MSB
+MOV DX,02FH
+IN AL,DX ; Get MSB of HWM Base Addr
+; EXIT CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,0AAH
+OUT DX,AL
Index: linux-2.6.10-mmh/drivers/i2c/chips/Kconfig
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/Kconfig 2004-12-26 19:39:51.000000000 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/Kconfig 2004-12-26 20:48:26.000000000 -0500
@@ -252,6 +252,18 @@ config SENSORS_SMSC47M1
This driver can also be built as a module. If so, the module
will be called smsc47m1.
+config SENSORS_SMSC47B397
+ tristate "SMSC LPC47B397-NC
+ depends on I2C && EXPERIMENTAL
+ select I2C_SENSOR
+ select I2C_ISA
+ help
+ If you say yes here you get support for the SMSC LPC47B397-NC
+ sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called smsc47b397.
+
config SENSORS_VIA686A
tristate "VIA686A"
depends on I2C && PCI && EXPERIMENTAL
Index: linux-2.6.10-mmh/drivers/i2c/chips/Makefile
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/Makefile 2004-12-26 19:39:51.000000000 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/Makefile 2004-12-26 20:48:26.000000000 -0500
@@ -31,6 +31,7 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
+obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
Index: linux-2.6.10-mmh/drivers/i2c/chips/smsc47b397.c
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/smsc47b397.c 2004-12-29 05:08:59.698570592 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/smsc47b397.c 2004-12-26 20:48:26.000000000 -0500
@@ -0,0 +1,352 @@
+/*
+ smsc47b397.c - Part of lm_sensors, Linux kernel modules
+ for hardware monitoring
+
+ Supports the SMSC LPC47B397-NC Super-I/O chip.
+
+ Author/Maintainer: Mark M. Hoffman <[email protected]>
+ Copyright (C) 2004 Utilitek Systems, Inc.
+
+ derived in part from smsc47m1.c:
+ Copyright (C) 2002 Mark D. Studebaker <[email protected]>
+ Copyright (C) 2004 Jean Delvare <[email protected]>
+
+ 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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* Address is autodetected, there is no default value */
+static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{NULL}};
+
+enum chips { any_chip, smsc47b397 };
+static struct i2c_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .normal_isa = normal_isa,
+ .probe = normal_i2c, /* cheat */
+ .ignore = normal_i2c, /* cheat */
+ .forces = forces,
+};
+
+/* Super-I/0 registers and commands */
+
+#define REG 0x2e /* The register to read/write */
+#define VAL 0x2f /* The value to read/write */
+
+static inline void superio_outb(int reg, int val)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+/* select superio logical device */
+static inline void superio_select(int ld)
+{
+ superio_outb(0x07, ld);
+}
+
+static inline void superio_enter(void)
+{
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0xAA, REG);
+}
+
+#define SUPERIO_REG_DEVID 0x20
+#define SUPERIO_REG_DEVREV 0x21
+#define SUPERIO_REG_BASE_MSB 0x60
+#define SUPERIO_REG_BASE_LSB 0x61
+#define SUPERIO_REG_LD8 0x08
+
+#define SMSC_EXTENT 0x02
+
+/* 0 <= nr <= 3 */
+static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
+#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)])
+
+/* 0 <= nr <= 3 */
+#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * nr)
+#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * nr)
+
+struct smsc47b397_data {
+ struct i2c_client client;
+ struct semaphore lock;
+
+ struct semaphore update_lock;
+ unsigned long last_updated; /* in jiffies */
+ int valid;
+
+ /* register values */
+ u16 fan[4];
+ u8 temp[4];
+};
+
+static int smsc47b397_read_value(struct i2c_client *client, u8 reg) +{
+ struct smsc47b397_data *data = i2c_get_clientdata(client);
+ int res;
+
+ down(&data->lock);
+ outb(reg, client->addr);
+ res = inb_p(client->addr + 1);
+ up(&data->lock);
+ return res;
+}
+
+static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) +{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smsc47b397_data *data = i2c_get_clientdata(client);
+ int i;
+
+ down(&data->update_lock);
+
+ if (time_after(jiffies - data->last_updated, (unsigned long)(HZ+HZ/2))
+ || time_before(jiffies, data->last_updated) || !data->valid) {
+
+ dev_dbg(&client->dev, "starting device update...\n");
+
+ /* 4 temperature inputs, 4 fan inputs */
+ for (i = 0; i < 4; i++) {
+ data->temp[i] = smsc47b397_read_value(client,
+ SMSC47B397_REG_TEMP(i));
+
+ /* must read LSB first */
+ data->fan[i] = smsc47b397_read_value(client,
+ SMSC47B397_REG_FAN_LSB(i));
+ data->fan[i] |= smsc47b397_read_value(client,
+ SMSC47B397_REG_FAN_MSB(i)) << 8;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+
+ dev_dbg(&client->dev, "... device update complete\n");
+ }
+
+ up(&data->update_lock);
+
+ return data;
+}
+
+/* TEMP: 0.001C/bit (-128C to +127C)
+ REG: 1C/bit, two's complement */
+static int temp_from_reg(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_temp(struct device *dev, char *buf, int nr) +{
+ struct smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
+}
+
+#define sysfs_temp(num) \
+static ssize_t show_temp##num(struct device *dev, char *buf) \
+{ \
+ return show_temp(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL) +
+sysfs_temp(1);
+sysfs_temp(2);
+sysfs_temp(3);
+sysfs_temp(4);
+
+#define device_create_file_temp(client, num) \
+ device_create_file(&client->dev, &dev_attr_temp##num##_input)
+
+/* FAN: 1 RPM/bit
+ REG: count of 90kHz pulses / revolution */
+static int fan_from_reg(u16 reg)
+{
+ return 90000 * 60 / reg;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+ struct smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr]));
+}
+
+#define sysfs_fan(num) \
+static ssize_t show_fan##num(struct device *dev, char *buf) \
+{ \
+ return show_fan(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL) +
+sysfs_fan(1);
+sysfs_fan(2);
+sysfs_fan(3);
+sysfs_fan(4);
+
+#define device_create_file_fan(client, num) \
+ device_create_file(&client->dev, &dev_attr_fan##num##_input)
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind); +
+static int smsc47b397_attach_adapter(struct i2c_adapter *adapter) +{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_detect(adapter, &addr_data, smsc47b397_detect);
+}
+
+static int smsc47b397_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ release_region(client->addr, SMSC_EXTENT);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static struct i2c_driver smsc47b397_driver = {
+ .owner = THIS_MODULE,
+ .name = "smsc47b397",
+ .id = I2C_DRIVERID_SMSC47B397,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = smsc47b397_attach_adapter,
+ .detach_client = smsc47b397_detach_client,
+};
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind) +{
+ struct i2c_client *new_client;
+ struct smsc47b397_data *data;
+ int err = 0;
+
+ if (!i2c_is_isa_adapter(adapter)) {
+ return 0;
+ }
+
+ if (!request_region(addr, SMSC_EXTENT, "smsc47b397")) {
+ dev_err(&adapter->dev, "Region 0x%x already in use!\n",addr);
+ return -EBUSY;
+ }
+
+ if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto error_release;
+ }
+ memset(data, 0x00, sizeof(struct smsc47b397_data));
+
+ new_client = &data->client;
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = addr;
+ init_MUTEX(&data->lock);
+ new_client->adapter = adapter;
+ new_client->driver = &smsc47b397_driver;
+ new_client->flags = 0;
+
+ strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
+
+ init_MUTEX(&data->update_lock);
+
+ if ((err = i2c_attach_client(new_client)))
+ goto error_free;
+
+ device_create_file_temp(new_client, 1);
+ device_create_file_temp(new_client, 2);
+ device_create_file_temp(new_client, 3);
+ device_create_file_temp(new_client, 4);
+
+ device_create_file_fan(new_client, 1);
+ device_create_file_fan(new_client, 2);
+ device_create_file_fan(new_client, 3);
+ device_create_file_fan(new_client, 4);
+
+ return 0;
+
+error_free:
+ kfree(new_client);
+error_release:
+ release_region(addr, SMSC_EXTENT);
+ return err;
+}
+
+static int __init smsc47b397_find(unsigned int *addr)
+{
+ u8 id, rev;
+
+ superio_enter();
+ id = superio_inb(SUPERIO_REG_DEVID);
+
+ if (id != 0x6f) {
+ superio_exit();
+ return -ENODEV;
+ }
+
+ rev = superio_inb(SUPERIO_REG_DEVREV);
+
+ superio_select(SUPERIO_REG_LD8);
+ *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
+ | superio_inb(SUPERIO_REG_BASE_LSB);
+
+ printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC "
+ "(base address 0x%04x, revision %u)\n", *addr, rev);
+
+ superio_exit();
+ return 0;
+}
+
+static int __init smsc47b397_init(void)
+{
+ if (smsc47b397_find(normal_isa)) {
+ return -ENODEV;
+ }
+
+ return i2c_add_driver(&smsc47b397_driver);
+}
+
+static void __exit smsc47b397_exit(void)
+{
+ i2c_del_driver(&smsc47b397_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <[email protected]>"); +MODULE_DESCRIPTION("SMSC LPC47B397 driver"); +MODULE_LICENSE("GPL");
+
+module_init(smsc47b397_init);
+module_exit(smsc47b397_exit);
Index: linux-2.6.10-mmh/include/linux/i2c-id.h
=================================--- linux-2.6.10-mmh.orig/include/linux/i2c-id.h 2004-12-26 19:38:39.000000000 -0500
+++ linux-2.6.10-mmh/include/linux/i2c-id.h 2004-12-26 20:48:26.000000000 -0500
@@ -166,6 +166,7 @@
#define I2C_DRIVERID_ASB100 1043
#define I2C_DRIVERID_FSCHER 1046
#define I2C_DRIVERID_W83L785TS 1047
+#define I2C_DRIVERID_SMSC47B397 1050
/*
* ---- Adapter types ----------------------------------------------------
Signature · 2 lines
Mark M. Hoffman [email protected]
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
Hi Mark,
See my comments inline.
Quoted 2 lines
Signed-off-by Craig Kelly <ckelly at ibnads d0t com>Signed-off-by Glenn Ball <glenn_ball at utilitek d0t com>
Hm. Greg won't like that, you know ;) (Nor do I for that matter. It's not the f4r w3st here! ;))
Quoted 10 lines
Documentation/i2c/chips/smsc47b397.txt +3
-0@@ -0,0 +1,128 @@ +November 23, 2004 + +The following specification describes the SMSC LPC47B397-NC sensorchip +(for which there is no public datasheet available). This document was +provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected +by Mark M. Hoffman <[email protected]>. (...)
I think that this document should be better formated (i.e. lines should be cut before column 80 when they are too long).
drivers/i2c/chips/Kconfig config SENSORS_SMSC47M1 · line 256
Quoted 8 lines
drivers/i2c/chips/Kconfig +2
-0@@ -252,6 +252,18 @@ config SENSORS_SMSC47M1 This driver can also be built as a module. If so, the module will be called smsc47m1. +config SENSORS_SMSC47B397 + tristate "SMSC LPC47B397-NC
Missing closing quotation mark.
drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_PCF8574) += pcf8574 · line 37
Quoted 10 lines
drivers/i2c/chips/Makefile +1
-0@@ -31,6 +31,7 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574 obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o +obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
Hm, alphabetical order?
drivers/i2c/chips/smsc47b397.c
Quoted 6 lines
drivers/i2c/chips/smsc47b397.c +3
-0+/* 0 <= nr <= 3 */ +#define SMSC47B397_REG_FAN_LSB (nr) (0x28 + 2 * nr) +#define SMSC47B397_REG_FAN_MSB (nr) (0x29 + 2 * nr)
Additional parentheses around "nr" please.
drivers/i2c/chips/smsc47b397.c
Quoted 3 lines
drivers/i2c/chips/smsc47b397.c +2
-0+static struct smsc47b397_data *smsc47b397_update_device (struct device *dev) + if (time_after(jiffies - data->last_updated, (unsigned long)(HZ+HZ/2))
Where do you get the 1.5 second from? Seems much to me for such a simple device, especially since it has an ISA access. I would hope 1 second is enough.
drivers/i2c/chips/smsc47b397.c
Quoted 4 lines
drivers/i2c/chips/smsc47b397.c +4
-0+if (!request_region (addr,SMSC_EXTENT ,"smsc47b397" )) { +dev_err (&adapter->dev,"Region 0x%x already in use!\n" ,addr); +return -EBUSY ; + }
I have sent a patch recently, which make all drivers request their region using *_driver.name instead of duplicating the name string:
http://archives.andrew.net.au/lm-sensors/msg28657.html
Care to do the same for this driver?
While you're at it, missing space before last comma.
drivers/i2c/chips/smsc47b397.c
Quoted 5 lines
drivers/i2c/chips/smsc47b397.c +5
-0+static int __init smsc47b397_init (void ) +{ +if (smsc47b397_find (normal_isa)) { +return -ENODEV ; + }
You could instead propagate the error code returned by smsc47b397_find.
Other than that, I'm fine with this driver. Nice work :)
Additional question:
This driver uses logical device 8, while sensors-detect probes device 0x0a. Is this a bug in sensors-detect?
Thanks,
Signature · 2 lines
Jean Delvare http://khali.linux-fr.org/
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi Jean:
(CC Craig Kelly who can skip to the very bottom of this message...)
Quoted 5 lines · Jean Delvare
Quoted 2 lines
Signed-off-by Craig Kelly <ckelly at ibnads d0t com>Signed-off-by Glenn Ball <glenn_ball at utilitek d0t com>Hm. Greg won't like that, you know ;) (Nor do I for that matter. It's not the f4r w3st here! ;))
Is there a published standard for email address obfuscation? Would it make sense if there was? ;)
Quoted 5 lines
Quoted 5 lines
Documentation/i2c/chips/smsc47b397.txt +2
-0@@ -0,0 +1,128 @@ +November 23, 2004 +
Quoted 8 lines
Quoted 5 lines
Documentation/i2c/chips/smsc47b397.txt +1
-0+The following specification describes the SMSC LPC47B397-NC sensorchip +(for which there is no public datasheet available). This document was +provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected +by Mark M. Hoffman <[email protected]>. (...)
I think that this document should be better formated (i.e. lines should be cut before column 80 when they are too long).
Fixed.
Quoted 10 lines
drivers/i2c/chips/Kconfig config SENSORS_SMSC47M1 · line 256
Quoted 8 lines
drivers/i2c/chips/Kconfig +2
-0@@ -252,6 +252,18 @@ config SENSORS_SMSC47M1 This driver can also be built as a module. If so, the module will be called smsc47m1. +config SENSORS_SMSC47B397 + tristate "SMSC LPC47B397-NCMissing closing quotation mark.
Fixed. Also moved to put in alpha. order.
Quoted 4 lines
Quoted 4 lines
drivers/i2c/chips/Makefile +0
-0@@ -31,6 +31,7 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574 obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
Quoted 8 lines
drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_PCF8574) += pcf8574 · line 37
Quoted 6 lines
drivers/i2c/chips/Makefile +1
-0obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o +obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.oHm, alphabetical order?
Fixed.
Quoted 8 lines
drivers/i2c/chips/smsc47b397.c
Quoted 6 lines
drivers/i2c/chips/smsc47b397.c +3
-0+/* 0 <= nr <= 3 */ +#define SMSC47B397_REG_FAN_LSB (nr) (0x28 + 2 * nr) +#define SMSC47B397_REG_FAN_MSB (nr) (0x29 + 2 * nr)Additional parentheses around "nr" please.
Fixed.
Quoted 7 lines
drivers/i2c/chips/smsc47b397.c
Quoted 3 lines
drivers/i2c/chips/smsc47b397.c +2
-0+static struct smsc47b397_data *smsc47b397_update_device (struct device *dev) + if (time_after(jiffies - data->last_updated, (unsigned long)(HZ+HZ/2))Where do you get the 1.5 second from? Seems much to me for such a simple device, especially since it has an ISA access. I would hope 1 second is enough.
Obviously not from the datasheet, heh. A lot of other drivers use that. I'm sure I cut-n-pasted without thinking much about it. I'll change it to 1 sec.
Quoted 9 lines
drivers/i2c/chips/smsc47b397.c
Quoted 4 lines
drivers/i2c/chips/smsc47b397.c +4
-0+if (!request_region (addr,SMSC_EXTENT ,"smsc47b397" )) { +dev_err (&adapter->dev,"Region 0x%x already in use!\n" ,addr); +return -EBUSY ; + }I have sent a patch recently, which make all drivers request their region using *_driver.name instead of duplicating the name string:
http://archives.andrew.net.au/lm-sensors/msg28657.html
Quoted 3 lines
Care to do the same for this driver?
While you're at it, missing space before last comma.
Fixed^2.
Quoted 7 lines
drivers/i2c/chips/smsc47b397.c
Quoted 5 lines
drivers/i2c/chips/smsc47b397.c +5
-0+static int __init smsc47b397_init (void ) +{ +if (smsc47b397_find (normal_isa)) { +return -ENODEV ; + }You could instead propagate the error code returned by
smsc47b397_find.
Done. Updated patch to follow...
Quoted 1 line
Other than that, I'm fine with this driver. Nice work :)
Thanks. :)
Quoted 3 lines
Additional question:
This driver uses logical device 8, while sensors-detect probes device0x0a. Is this a bug in sensors-detect?
Yes, and that's a great catch by the way (since that didn't even appear in this patch). I only tested the sensors-detect patch on real h/w once, and it worked that way... but in the driver I don't do any activation test anyway (as it's not specified in the "datasheet")... reading further, I see that it's claimed to be present on all ISA PnP so I guess it can't hurt to have it in sensors-detect.
Craig: I'll commit a patch to sensors-detect tonight. I would appreciate if you try it again on the actual hardware and let me know if it fails.
Thanks & regards,
Signature · 2 lines
Mark M. Hoffman [email protected]
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi Greg:
This patch (take 2) adds support for the SMSC LPC47B397-NC sensor chip. It was sponsored by In-Store Broadcasting Network.
Index: linux-2.6.10-mmh/Documentation/i2c/chips/smsc47b397.txt
=================================--- linux-2.6.10-mmh.orig/Documentation/i2c/chips/smsc47b397.txt 2004-12-29 05:08:59.698570592 -0500
+++ linux-2.6.10-mmh/Documentation/i2c/chips/smsc47b397.txt 2005-01-03 21:56:20.000000000 -0500
@@ -0,0 +1,146 @@
+November 23, 2004
+
+The following specification describes the SMSC LPC47B397-NC sensor chip +(for which there is no public datasheet available). This document was +provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected +by Mark M. Hoffman <[email protected]>.
+
+* * * * *
+
+Methods for detecting the HP SIO and reading the thermal data on a dc7100. +
+The thermal information on the dc7100 is contained in the SIO Hardware Monitor +(HWM). The information is accessed through an index/data pair. The index/data +pair is located at the HWM Base Address + 0 and the HWM Base Address + 1. The +HWM Base address can be obtained from Logical Device 8, registers 0x60 (MSB) +and 0x61 (LSB). Currently we are using 0x480 for the HWM Base Address and +0x480 and 0x481 for the index/data pair.
+
+Reading temperature information.
+The temperature information is located in the following registers:
+Temp1 0x25 (Currently, this reflects the CPU temp on all systems).
+Temp2 0x26
+Temp3 0x27
+Temp4 0x80
+
+Programming Example
+The following is an example of how to read the HWM temperature registers:
+MOV DX,480H
+MOV AX,25H
+OUT DX,AL
+MOV DX,481H
+IN AL,DX
+
+AL contains the data in hex, the temperature in Celsius is the decimal +equivalent.
+
+Ex: If AL contains 0x2A, the temperature is 42 degrees C.
+
+Reading tach information.
+The fan speed information is located in the following registers:
+ LSB MSB
+Tach1 0x28 0x29 (Currently, this reflects the CPU
+ fan speed on all systems).
+Tach2 0x2A 0x2B
+Tach3 0x2C 0x2D
+Tach4 0x2E 0x2F
+
+Important!!!
+Reading the tach LSB locks the tach MSB.
+The LSB Must be read first.
+
+How to convert the tach reading to RPM.
+The tach reading (TCount) is given by: (Tach MSB * 256) + (Tach LSB) +The SIO counts the number of 90kHz (11.111us) pulses per revolution. +RPM = 60/(TCount * 11.111us)
+
+Example:
+Reg 0x28 = 0x9B
+Reg 0x29 = 0x08
+
+TCount = 0x89B = 2203
+
+RPM = 60 / (2203 * 11.11111 E-6) = 2451 RPM
+
+Obtaining the SIO version.
+
+CONFIGURATION SEQUENCE
+To program the configuration registers, the following sequence must be followed:
+1. Enter Configuration Mode
+2. Configure the Configuration Registers
+3. Exit Configuration Mode.
+
+Enter Configuration Mode
+To place the chip into the Configuration State The config key (0x55) is written +to the CONFIG PORT (0x2E).
+
+Configuration Mode
+In configuration mode, the INDEX PORT is located at the CONFIG PORT address and +the DATA PORT is at INDEX PORT address + 1.
+
+The desired configuration registers are accessed in two steps:
+a. Write the index of the Logical Device Number Configuration Register
+ (i.e., 0x07) to the INDEX PORT and then write the number of the
+ desired logical device to the DATA PORT.
+
+b. Write the address of the desired configuration register within the
+ logical device to the INDEX PORT and then write or read the config-
+ uration register through the DATA PORT.
+
+Note: If accessing the Global Configuration Registers, step (a) is not required. +
+Exit Configuration Mode
+To exit the Configuration State the write 0xAA to the CONFIG PORT (0x2E). +The chip returns to the RUN State. (This is important).
+
+Programming Example
+The following is an example of how to read the SIO Device ID located at 0x20 +
+; ENTER CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,055H
+OUT DX,AL
+; GLOBAL CONFIGURATION REGISTER
+MOV DX,02EH
+MOV AL,20H
+OUT DX,AL
+; READ THE DATA
+MOV DX,02FH
+IN AL,DX
+; EXIT CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,0AAH
+OUT DX,AL
+
+The registers of interest for identifying the SIO on the dc7100 are Device ID +(0x20) and Device Rev (0x21).
+
+The Device ID will read 0X6F
+The Device Rev currently reads 0x01
+
+Obtaining the HWM Base Address.
+The following is an example of how to read the HWM Base Address located in +Logical Device 8.
+
+; ENTER CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,055H
+OUT DX,AL
+; CONFIGURE REGISTER CRE0,
+; LOGICAL DEVICE 8
+MOV DX,02EH
+MOV AL,07H
+OUT DX,AL ;Point to LD# Config Reg
+MOV DX,02FH
+MOV AL, 08H
+OUT DX,AL;Point to Logical Device 8
+;
+MOV DX,02EH
+MOV AL,60H
+OUT DX,AL ; Point to HWM Base Addr MSB
+MOV DX,02FH
+IN AL,DX ; Get MSB of HWM Base Addr
+; EXIT CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,0AAH
+OUT DX,AL
Index: linux-2.6.10-mmh/drivers/i2c/chips/Kconfig
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/Kconfig 2004-12-26 19:39:51.000000000 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/Kconfig 2005-01-03 21:59:59.000000000 -0500
@@ -239,6 +239,18 @@ config SENSORS_PC87360
This driver can also be built as a module. If so, the module will be called pc87360.
+config SENSORS_SMSC47B397
+ tristate "SMSC LPC47B397-NC"
+ depends on I2C && EXPERIMENTAL
+ select I2C_SENSOR
+ select I2C_ISA
+ help
+ If you say yes here you get support for the SMSC LPC47B397-NC
+ sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called smsc47b397.
+
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles" depends on I2C && EXPERIMENTAL
Index: linux-2.6.10-mmh/drivers/i2c/chips/Makefile
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/Makefile 2004-12-26 19:39:51.000000000 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/Makefile 2005-01-03 22:00:53.000000000 -0500
@@ -30,6 +30,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o
+obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
Index: linux-2.6.10-mmh/drivers/i2c/chips/smsc47b397.c
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/smsc47b397.c 2004-12-29 05:08:59.698570592 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/smsc47b397.c 2005-01-03 22:52:40.984834110 -0500
@@ -0,0 +1,353 @@
+/*
+ smsc47b397.c - Part of lm_sensors, Linux kernel modules
+ for hardware monitoring
+
+ Supports the SMSC LPC47B397-NC Super-I/O chip.
+
+ Author/Maintainer: Mark M. Hoffman <[email protected]>
+ Copyright (C) 2004 Utilitek Systems, Inc.
+
+ derived in part from smsc47m1.c:
+ Copyright (C) 2002 Mark D. Studebaker <[email protected]>
+ Copyright (C) 2004 Jean Delvare <[email protected]>
+
+ 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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* Address is autodetected, there is no default value */
+static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{NULL}};
+
+enum chips { any_chip, smsc47b397 };
+static struct i2c_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .normal_isa = normal_isa,
+ .probe = normal_i2c, /* cheat */
+ .ignore = normal_i2c, /* cheat */
+ .forces = forces,
+};
+
+/* Super-I/0 registers and commands */
+
+#define REG 0x2e /* The register to read/write */
+#define VAL 0x2f /* The value to read/write */
+
+static inline void superio_outb(int reg, int val)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+/* select superio logical device */
+static inline void superio_select(int ld)
+{
+ superio_outb(0x07, ld);
+}
+
+static inline void superio_enter(void)
+{
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0xAA, REG);
+}
+
+#define SUPERIO_REG_DEVID 0x20
+#define SUPERIO_REG_DEVREV 0x21
+#define SUPERIO_REG_BASE_MSB 0x60
+#define SUPERIO_REG_BASE_LSB 0x61
+#define SUPERIO_REG_LD8 0x08
+
+#define SMSC_EXTENT 0x02
+
+/* 0 <= nr <= 3 */
+static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
+#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)])
+
+/* 0 <= nr <= 3 */
+#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr))
+#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
+
+struct smsc47b397_data {
+ struct i2c_client client;
+ struct semaphore lock;
+
+ struct semaphore update_lock;
+ unsigned long last_updated; /* in jiffies */
+ int valid;
+
+ /* register values */
+ u16 fan[4];
+ u8 temp[4];
+};
+
+static int smsc47b397_read_value(struct i2c_client *client, u8 reg) +{
+ struct smsc47b397_data *data = i2c_get_clientdata(client);
+ int res;
+
+ down(&data->lock);
+ outb(reg, client->addr);
+ res = inb_p(client->addr + 1);
+ up(&data->lock);
+ return res;
+}
+
+static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) +{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smsc47b397_data *data = i2c_get_clientdata(client);
+ int i;
+
+ down(&data->update_lock);
+
+ if (time_after(jiffies - data->last_updated, (unsigned long)HZ)
+ || time_before(jiffies, data->last_updated) || !data->valid) {
+
+ dev_dbg(&client->dev, "starting device update...\n");
+
+ /* 4 temperature inputs, 4 fan inputs */
+ for (i = 0; i < 4; i++) {
+ data->temp[i] = smsc47b397_read_value(client,
+ SMSC47B397_REG_TEMP(i));
+
+ /* must read LSB first */
+ data->fan[i] = smsc47b397_read_value(client,
+ SMSC47B397_REG_FAN_LSB(i));
+ data->fan[i] |= smsc47b397_read_value(client,
+ SMSC47B397_REG_FAN_MSB(i)) << 8;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+
+ dev_dbg(&client->dev, "... device update complete\n");
+ }
+
+ up(&data->update_lock);
+
+ return data;
+}
+
+/* TEMP: 0.001C/bit (-128C to +127C)
+ REG: 1C/bit, two's complement */
+static int temp_from_reg(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_temp(struct device *dev, char *buf, int nr) +{
+ struct smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
+}
+
+#define sysfs_temp(num) \
+static ssize_t show_temp##num(struct device *dev, char *buf) \
+{ \
+ return show_temp(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL) +
+sysfs_temp(1);
+sysfs_temp(2);
+sysfs_temp(3);
+sysfs_temp(4);
+
+#define device_create_file_temp(client, num) \
+ device_create_file(&client->dev, &dev_attr_temp##num##_input)
+
+/* FAN: 1 RPM/bit
+ REG: count of 90kHz pulses / revolution */
+static int fan_from_reg(u16 reg)
+{
+ return 90000 * 60 / reg;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+ struct smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr]));
+}
+
+#define sysfs_fan(num) \
+static ssize_t show_fan##num(struct device *dev, char *buf) \
+{ \
+ return show_fan(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL) +
+sysfs_fan(1);
+sysfs_fan(2);
+sysfs_fan(3);
+sysfs_fan(4);
+
+#define device_create_file_fan(client, num) \
+ device_create_file(&client->dev, &dev_attr_fan##num##_input)
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind); +
+static int smsc47b397_attach_adapter(struct i2c_adapter *adapter) +{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_detect(adapter, &addr_data, smsc47b397_detect);
+}
+
+static int smsc47b397_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ release_region(client->addr, SMSC_EXTENT);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static struct i2c_driver smsc47b397_driver = {
+ .owner = THIS_MODULE,
+ .name = "smsc47b397",
+ .id = I2C_DRIVERID_SMSC47B397,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = smsc47b397_attach_adapter,
+ .detach_client = smsc47b397_detach_client,
+};
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind) +{
+ struct i2c_client *new_client;
+ struct smsc47b397_data *data;
+ int err = 0;
+
+ if (!i2c_is_isa_adapter(adapter)) {
+ return 0;
+ }
+
+ if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) {
+ dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr);
+ return -EBUSY;
+ }
+
+ if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto error_release;
+ }
+ memset(data, 0x00, sizeof(struct smsc47b397_data));
+
+ new_client = &data->client;
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = addr;
+ init_MUTEX(&data->lock);
+ new_client->adapter = adapter;
+ new_client->driver = &smsc47b397_driver;
+ new_client->flags = 0;
+
+ strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
+
+ init_MUTEX(&data->update_lock);
+
+ if ((err = i2c_attach_client(new_client)))
+ goto error_free;
+
+ device_create_file_temp(new_client, 1);
+ device_create_file_temp(new_client, 2);
+ device_create_file_temp(new_client, 3);
+ device_create_file_temp(new_client, 4);
+
+ device_create_file_fan(new_client, 1);
+ device_create_file_fan(new_client, 2);
+ device_create_file_fan(new_client, 3);
+ device_create_file_fan(new_client, 4);
+
+ return 0;
+
+error_free:
+ kfree(new_client);
+error_release:
+ release_region(addr, SMSC_EXTENT);
+ return err;
+}
+
+static int __init smsc47b397_find(unsigned int *addr)
+{
+ u8 id, rev;
+
+ superio_enter();
+ id = superio_inb(SUPERIO_REG_DEVID);
+
+ if (id != 0x6f) {
+ superio_exit();
+ return -ENODEV;
+ }
+
+ rev = superio_inb(SUPERIO_REG_DEVREV);
+
+ superio_select(SUPERIO_REG_LD8);
+ *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
+ | superio_inb(SUPERIO_REG_BASE_LSB);
+
+ printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC "
+ "(base address 0x%04x, revision %u)\n", *addr, rev);
+
+ superio_exit();
+ return 0;
+}
+
+static int __init smsc47b397_init(void)
+{
+ int ret;
+
+ if ((ret = smsc47b397_find(normal_isa)))
+ return ret;
+
+ return i2c_add_driver(&smsc47b397_driver);
+}
+
+static void __exit smsc47b397_exit(void)
+{
+ i2c_del_driver(&smsc47b397_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <[email protected]>"); +MODULE_DESCRIPTION("SMSC LPC47B397 driver"); +MODULE_LICENSE("GPL");
+
+module_init(smsc47b397_init);
+module_exit(smsc47b397_exit);
Index: linux-2.6.10-mmh/include/linux/i2c-id.h
=================================--- linux-2.6.10-mmh.orig/include/linux/i2c-id.h 2004-12-26 19:38:39.000000000 -0500
+++ linux-2.6.10-mmh/include/linux/i2c-id.h 2004-12-26 20:48:26.000000000 -0500
@@ -166,6 +166,7 @@
#define I2C_DRIVERID_ASB100 1043
#define I2C_DRIVERID_FSCHER 1046
#define I2C_DRIVERID_W83L785TS 1047
+#define I2C_DRIVERID_SMSC47B397 1050
/*
* ---- Adapter types ----------------------------------------------------
Signature · 2 lines
Mark M. Hoffman [email protected]
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi Mark,
Quoted 2 lines
Is there a published standard for email address obfuscation? Would it make sense if there was? ;)
I tried a simple (cowboyz-free) obfuscation in the early days but Greg told me it was making things more complex for him, so I stopped. This probably means that people not wanting their e-mail address known just cannot sign kernel patches.
Signature · 2 lines
Jean Delvare http://khali.linux-fr.org/
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi again:
Quoted 7 lines · Jean Delvare
Quoted 2 lines
Is there a published standard for email address obfuscation? Would it make sense if there was? ;)
I tried a simple (cowboyz-free) obfuscation in the early days but Greg told me it was making things more complex for him, so I stopped. This probably means that people not wanting their e-mail address known just cannot sign kernel patches.
How about this:
The first two aren't going to maintain the driver anyway, but I want the signatures there as contributor of the datasheet and holder of the copyright, respectively. If I'm going to "sign-off" that I am allowed to distribute this work, then I also want to include from whom this permission came. Linus wrote [1]:
Quoted 4 lines
The above also allows for companies that have "release criteria" to have the company "release person" sign off on a patch, so that a company can easily incorporate their own internal release procedures and see that all the patches have gone through the right channel. [...]
Therefore, I think Linus intended for us to be able to sign off this way.
[1] http://lwn.net/Articles/86436/
Regards,
Signature · 2 lines
Mark M. Hoffman [email protected]
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi Mark,
Quoted 12 lines
How about this:
Signed-off-by Craig Kelly (In-Store Broadcasting Network)Signed-off-by Glenn Ball (Utilitek Systems, Inc.)Signed-off-by Mark M. Hoffman <[email protected]>The first two aren't going to maintain the driver anyway, but I want the signatures there as contributor of the datasheet and holder of the copyright, respectively. If I'm going to "sign-off" that I am allowed to distribute this work, then I also want to include from whom this permission came. Linus wrote [1]:
Quoted 8 lines
Quoted 5 lines
The above also allows for companies that have "release criteria" to have the company "release person" sign off on a patch, so that a company can easily incorporate their own internal release procedures and see that all the patches have gone through the right channel. [...]
Therefore, I think Linus intended for us to be able to sign off this way.
Fine with me! But Greg decides, of course.
Signature · 2 lines
Jean Delvare http://khali.linux-fr.org/
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanQuoted 20 lines · Mark M. Hoffman
Hi Jean:
(CC Craig Kelly who can skip to the very bottom of this message...)
Quoted 5 lines · Jean Delvare
Additional question:
This driver uses logical device 8, while sensors-detect probes device0x0a. Is this a bug in sensors-detect?Yes, and that's a great catch by the way (since that didn't even appear in this patch). I only tested the sensors-detect patch on real h/w once, and it worked that way... but in the driver I don't do any activation test anyway (as it's not specified in the "datasheet")... reading further, I see that it's claimed to be present on all ISA PnP so I guess it can't hurt to have it in sensors-detect.
Quoted 4 lines
Craig: I'll commit a patch to sensors-detect tonight. I would appreciate if you try it again on the actual hardware and let me know if it fails.
Thanks; just let me know when to sync with CVS and I'll give it a go.
--
Craig Kelley
In-Store Broadcasting Network
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20050104/7b2714b8/attachment.html
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanQuoted 17 lines · Mark M. Hoffman
Hi again:
Quoted 7 lines · Jean Delvare
Quoted 2 lines
Is there a published standard for email address obfuscation? Would it make sense if there was? ;)
I tried a simple (cowboyz-free) obfuscation in the early days but Greg told me it was making things more complex for him, so I stopped. This probably means that people not wanting their e-mail address known just cannot sign kernel patches.
How about this:
Signed-off-by Craig Kelly (In-Store Broadcasting Network)Signed-off-by Glenn Ball (Utilitek Systems, Inc.)Signed-off-by Mark M. Hoffman <[email protected]>
Quoted 5 lines
The first two aren't going to maintain the driver anyway, but I want the signatures there as contributor of the datasheet and holder of the copyright, respectively. If I'm going to "sign-off" that I am allowed to distribute this work, then I also want to include from whom this permission came. Linus wrote [1]:
That's an ok way to do it, but I _really_ would like to see real email addresses in the signed-off-by line, as it helps out in the end for figuring out problems.
Care to redo the patch?
thanks,
greg k-h
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi Greg:
Quoted 11 lines · I wrote:
Quoted 11 lines
How about this:
Signed-off-by Craig Kelly (In-Store Broadcasting Network)Signed-off-by Glenn Ball (Utilitek Systems, Inc.)Signed-off-by Mark M. Hoffman <[email protected]>The first two aren't going to maintain the driver anyway, but I want the signatures there as contributor of the datasheet and holder of the copyright, respectively. If I'm going to "sign-off" that I am allowed to distribute this work, then I also want to include from whom this permission came. Linus wrote [1]:
Quoted 3 lines · Greg KH
That's an ok way to do it, but I _really_ would like to see real email addresses in the signed-off-by line, as it helps out in the end for figuring out problems.
I understand that one motivation for signed-off-by is to have a follow-up path for technical problems... why else do you need an email address?
I can't speak for Craig, but Glenn Ball (my boss) does not want to get email about technical problems with this driver. Or just in case you're talking about legal problems, lawyers can well enough find contact info based on those lines. If you absolutely insist, then I would rather remove at least Glenn's line, and I'll let Craig speak for himself.
Regards,
Signature · 2 lines
Mark M. Hoffman [email protected]
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanQuoted 15 lines · Mark M. Hoffman
Hi Greg:
Quoted 11 lines · I wrote:
Quoted 11 lines
How about this:
Signed-off-by Craig Kelly (In-Store Broadcasting Network)Signed-off-by Glenn Ball (Utilitek Systems, Inc.)Signed-off-by Mark M. Hoffman <[email protected]>The first two aren't going to maintain the driver anyway, but I want the signatures there as contributor of the datasheet and holder of the copyright, respectively. If I'm going to "sign-off" that I am allowed to distribute this work, then I also want to include from whom this permission came. Linus wrote [1]:
Quoted 7 lines
Quoted 3 lines · Greg KH
That's an ok way to do it, but I _really_ would like to see real email addresses in the signed-off-by line, as it helps out in the end for figuring out problems.
I understand that one motivation for signed-off-by is to have a follow-up path for technical problems... why else do you need an email address?
It's easier to show "who did what" by an email address. That's why we ask for it. See the shortlog program that we have to track email addresses to names for an example of this mapping.
Quoted 5 lines
I can't speak for Craig, but Glenn Ball (my boss) does not want to get email about technical problems with this driver. Or just in case you're talking about legal problems, lawyers can well enough find contact info based on those lines. If you absolutely insist, then I would rather remove at least Glenn's line, and I'll let Craig speak for himself.
No, I'm not insisting, the above change is fine with me, I just will not accept the obfusciated version you originally posted, as that's just trying to hide something that should not be hidden (hey, spam filters are free...)
thanks,
greg k-h
PATCH 1/1 new sensors driver: SMSC LPC47B397-NC
In reply to Mark M. HoffmanHi Greg:
This patch (3rd time's a charm) adds support for the SMSC LPC47B397-NC sensor chip. It was sponsored by In-Store Broadcasting Network.
Index: linux-2.6.10-mmh/Documentation/i2c/chips/smsc47b397.txt
=================================--- linux-2.6.10-mmh.orig/Documentation/i2c/chips/smsc47b397.txt 2004-12-29 05:08:59.698570592 -0500
+++ linux-2.6.10-mmh/Documentation/i2c/chips/smsc47b397.txt 2005-01-03 21:56:20.000000000 -0500
@@ -0,0 +1,146 @@
+November 23, 2004
+
+The following specification describes the SMSC LPC47B397-NC sensor chip +(for which there is no public datasheet available). This document was +provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected +by Mark M. Hoffman <[email protected]>.
+
+* * * * *
+
+Methods for detecting the HP SIO and reading the thermal data on a dc7100. +
+The thermal information on the dc7100 is contained in the SIO Hardware Monitor +(HWM). The information is accessed through an index/data pair. The index/data +pair is located at the HWM Base Address + 0 and the HWM Base Address + 1. The +HWM Base address can be obtained from Logical Device 8, registers 0x60 (MSB) +and 0x61 (LSB). Currently we are using 0x480 for the HWM Base Address and +0x480 and 0x481 for the index/data pair.
+
+Reading temperature information.
+The temperature information is located in the following registers:
+Temp1 0x25 (Currently, this reflects the CPU temp on all systems).
+Temp2 0x26
+Temp3 0x27
+Temp4 0x80
+
+Programming Example
+The following is an example of how to read the HWM temperature registers:
+MOV DX,480H
+MOV AX,25H
+OUT DX,AL
+MOV DX,481H
+IN AL,DX
+
+AL contains the data in hex, the temperature in Celsius is the decimal +equivalent.
+
+Ex: If AL contains 0x2A, the temperature is 42 degrees C.
+
+Reading tach information.
+The fan speed information is located in the following registers:
+ LSB MSB
+Tach1 0x28 0x29 (Currently, this reflects the CPU
+ fan speed on all systems).
+Tach2 0x2A 0x2B
+Tach3 0x2C 0x2D
+Tach4 0x2E 0x2F
+
+Important!!!
+Reading the tach LSB locks the tach MSB.
+The LSB Must be read first.
+
+How to convert the tach reading to RPM.
+The tach reading (TCount) is given by: (Tach MSB * 256) + (Tach LSB) +The SIO counts the number of 90kHz (11.111us) pulses per revolution. +RPM = 60/(TCount * 11.111us)
+
+Example:
+Reg 0x28 = 0x9B
+Reg 0x29 = 0x08
+
+TCount = 0x89B = 2203
+
+RPM = 60 / (2203 * 11.11111 E-6) = 2451 RPM
+
+Obtaining the SIO version.
+
+CONFIGURATION SEQUENCE
+To program the configuration registers, the following sequence must be followed:
+1. Enter Configuration Mode
+2. Configure the Configuration Registers
+3. Exit Configuration Mode.
+
+Enter Configuration Mode
+To place the chip into the Configuration State The config key (0x55) is written +to the CONFIG PORT (0x2E).
+
+Configuration Mode
+In configuration mode, the INDEX PORT is located at the CONFIG PORT address and +the DATA PORT is at INDEX PORT address + 1.
+
+The desired configuration registers are accessed in two steps:
+a. Write the index of the Logical Device Number Configuration Register
+ (i.e., 0x07) to the INDEX PORT and then write the number of the
+ desired logical device to the DATA PORT.
+
+b. Write the address of the desired configuration register within the
+ logical device to the INDEX PORT and then write or read the config-
+ uration register through the DATA PORT.
+
+Note: If accessing the Global Configuration Registers, step (a) is not required. +
+Exit Configuration Mode
+To exit the Configuration State the write 0xAA to the CONFIG PORT (0x2E). +The chip returns to the RUN State. (This is important).
+
+Programming Example
+The following is an example of how to read the SIO Device ID located at 0x20 +
+; ENTER CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,055H
+OUT DX,AL
+; GLOBAL CONFIGURATION REGISTER
+MOV DX,02EH
+MOV AL,20H
+OUT DX,AL
+; READ THE DATA
+MOV DX,02FH
+IN AL,DX
+; EXIT CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,0AAH
+OUT DX,AL
+
+The registers of interest for identifying the SIO on the dc7100 are Device ID +(0x20) and Device Rev (0x21).
+
+The Device ID will read 0X6F
+The Device Rev currently reads 0x01
+
+Obtaining the HWM Base Address.
+The following is an example of how to read the HWM Base Address located in +Logical Device 8.
+
+; ENTER CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,055H
+OUT DX,AL
+; CONFIGURE REGISTER CRE0,
+; LOGICAL DEVICE 8
+MOV DX,02EH
+MOV AL,07H
+OUT DX,AL ;Point to LD# Config Reg
+MOV DX,02FH
+MOV AL, 08H
+OUT DX,AL;Point to Logical Device 8
+;
+MOV DX,02EH
+MOV AL,60H
+OUT DX,AL ; Point to HWM Base Addr MSB
+MOV DX,02FH
+IN AL,DX ; Get MSB of HWM Base Addr
+; EXIT CONFIGURATION MODE
+MOV DX,02EH
+MOV AX,0AAH
+OUT DX,AL
Index: linux-2.6.10-mmh/drivers/i2c/chips/Kconfig
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/Kconfig 2004-12-26 19:39:51.000000000 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/Kconfig 2005-01-03 21:59:59.000000000 -0500
@@ -239,6 +239,18 @@ config SENSORS_PC87360
This driver can also be built as a module. If so, the module will be called pc87360.
+config SENSORS_SMSC47B397
+ tristate "SMSC LPC47B397-NC"
+ depends on I2C && EXPERIMENTAL
+ select I2C_SENSOR
+ select I2C_ISA
+ help
+ If you say yes here you get support for the SMSC LPC47B397-NC
+ sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called smsc47b397.
+
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles" depends on I2C && EXPERIMENTAL
Index: linux-2.6.10-mmh/drivers/i2c/chips/Makefile
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/Makefile 2004-12-26 19:39:51.000000000 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/Makefile 2005-01-03 22:00:53.000000000 -0500
@@ -30,6 +30,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o
+obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
Index: linux-2.6.10-mmh/drivers/i2c/chips/smsc47b397.c
=================================--- linux-2.6.10-mmh.orig/drivers/i2c/chips/smsc47b397.c 2004-12-29 05:08:59.698570592 -0500
+++ linux-2.6.10-mmh/drivers/i2c/chips/smsc47b397.c 2005-01-03 22:52:40.984834110 -0500
@@ -0,0 +1,353 @@
+/*
+ smsc47b397.c - Part of lm_sensors, Linux kernel modules
+ for hardware monitoring
+
+ Supports the SMSC LPC47B397-NC Super-I/O chip.
+
+ Author/Maintainer: Mark M. Hoffman <[email protected]>
+ Copyright (C) 2004 Utilitek Systems, Inc.
+
+ derived in part from smsc47m1.c:
+ Copyright (C) 2002 Mark D. Studebaker <[email protected]>
+ Copyright (C) 2004 Jean Delvare <[email protected]>
+
+ 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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* Address is autodetected, there is no default value */
+static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{NULL}};
+
+enum chips { any_chip, smsc47b397 };
+static struct i2c_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .normal_isa = normal_isa,
+ .probe = normal_i2c, /* cheat */
+ .ignore = normal_i2c, /* cheat */
+ .forces = forces,
+};
+
+/* Super-I/0 registers and commands */
+
+#define REG 0x2e /* The register to read/write */
+#define VAL 0x2f /* The value to read/write */
+
+static inline void superio_outb(int reg, int val)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+/* select superio logical device */
+static inline void superio_select(int ld)
+{
+ superio_outb(0x07, ld);
+}
+
+static inline void superio_enter(void)
+{
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0xAA, REG);
+}
+
+#define SUPERIO_REG_DEVID 0x20
+#define SUPERIO_REG_DEVREV 0x21
+#define SUPERIO_REG_BASE_MSB 0x60
+#define SUPERIO_REG_BASE_LSB 0x61
+#define SUPERIO_REG_LD8 0x08
+
+#define SMSC_EXTENT 0x02
+
+/* 0 <= nr <= 3 */
+static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
+#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)])
+
+/* 0 <= nr <= 3 */
+#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr))
+#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
+
+struct smsc47b397_data {
+ struct i2c_client client;
+ struct semaphore lock;
+
+ struct semaphore update_lock;
+ unsigned long last_updated; /* in jiffies */
+ int valid;
+
+ /* register values */
+ u16 fan[4];
+ u8 temp[4];
+};
+
+static int smsc47b397_read_value(struct i2c_client *client, u8 reg) +{
+ struct smsc47b397_data *data = i2c_get_clientdata(client);
+ int res;
+
+ down(&data->lock);
+ outb(reg, client->addr);
+ res = inb_p(client->addr + 1);
+ up(&data->lock);
+ return res;
+}
+
+static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) +{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smsc47b397_data *data = i2c_get_clientdata(client);
+ int i;
+
+ down(&data->update_lock);
+
+ if (time_after(jiffies - data->last_updated, (unsigned long)HZ)
+ || time_before(jiffies, data->last_updated) || !data->valid) {
+
+ dev_dbg(&client->dev, "starting device update...\n");
+
+ /* 4 temperature inputs, 4 fan inputs */
+ for (i = 0; i < 4; i++) {
+ data->temp[i] = smsc47b397_read_value(client,
+ SMSC47B397_REG_TEMP(i));
+
+ /* must read LSB first */
+ data->fan[i] = smsc47b397_read_value(client,
+ SMSC47B397_REG_FAN_LSB(i));
+ data->fan[i] |= smsc47b397_read_value(client,
+ SMSC47B397_REG_FAN_MSB(i)) << 8;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+
+ dev_dbg(&client->dev, "... device update complete\n");
+ }
+
+ up(&data->update_lock);
+
+ return data;
+}
+
+/* TEMP: 0.001C/bit (-128C to +127C)
+ REG: 1C/bit, two's complement */
+static int temp_from_reg(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_temp(struct device *dev, char *buf, int nr) +{
+ struct smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
+}
+
+#define sysfs_temp(num) \
+static ssize_t show_temp##num(struct device *dev, char *buf) \
+{ \
+ return show_temp(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL) +
+sysfs_temp(1);
+sysfs_temp(2);
+sysfs_temp(3);
+sysfs_temp(4);
+
+#define device_create_file_temp(client, num) \
+ device_create_file(&client->dev, &dev_attr_temp##num##_input)
+
+/* FAN: 1 RPM/bit
+ REG: count of 90kHz pulses / revolution */
+static int fan_from_reg(u16 reg)
+{
+ return 90000 * 60 / reg;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+ struct smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr]));
+}
+
+#define sysfs_fan(num) \
+static ssize_t show_fan##num(struct device *dev, char *buf) \
+{ \
+ return show_fan(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL) +
+sysfs_fan(1);
+sysfs_fan(2);
+sysfs_fan(3);
+sysfs_fan(4);
+
+#define device_create_file_fan(client, num) \
+ device_create_file(&client->dev, &dev_attr_fan##num##_input)
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind); +
+static int smsc47b397_attach_adapter(struct i2c_adapter *adapter) +{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_detect(adapter, &addr_data, smsc47b397_detect);
+}
+
+static int smsc47b397_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ release_region(client->addr, SMSC_EXTENT);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static struct i2c_driver smsc47b397_driver = {
+ .owner = THIS_MODULE,
+ .name = "smsc47b397",
+ .id = I2C_DRIVERID_SMSC47B397,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = smsc47b397_attach_adapter,
+ .detach_client = smsc47b397_detach_client,
+};
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind) +{
+ struct i2c_client *new_client;
+ struct smsc47b397_data *data;
+ int err = 0;
+
+ if (!i2c_is_isa_adapter(adapter)) {
+ return 0;
+ }
+
+ if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) {
+ dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr);
+ return -EBUSY;
+ }
+
+ if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto error_release;
+ }
+ memset(data, 0x00, sizeof(struct smsc47b397_data));
+
+ new_client = &data->client;
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = addr;
+ init_MUTEX(&data->lock);
+ new_client->adapter = adapter;
+ new_client->driver = &smsc47b397_driver;
+ new_client->flags = 0;
+
+ strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
+
+ init_MUTEX(&data->update_lock);
+
+ if ((err = i2c_attach_client(new_client)))
+ goto error_free;
+
+ device_create_file_temp(new_client, 1);
+ device_create_file_temp(new_client, 2);
+ device_create_file_temp(new_client, 3);
+ device_create_file_temp(new_client, 4);
+
+ device_create_file_fan(new_client, 1);
+ device_create_file_fan(new_client, 2);
+ device_create_file_fan(new_client, 3);
+ device_create_file_fan(new_client, 4);
+
+ return 0;
+
+error_free:
+ kfree(new_client);
+error_release:
+ release_region(addr, SMSC_EXTENT);
+ return err;
+}
+
+static int __init smsc47b397_find(unsigned int *addr)
+{
+ u8 id, rev;
+
+ superio_enter();
+ id = superio_inb(SUPERIO_REG_DEVID);
+
+ if (id != 0x6f) {
+ superio_exit();
+ return -ENODEV;
+ }
+
+ rev = superio_inb(SUPERIO_REG_DEVREV);
+
+ superio_select(SUPERIO_REG_LD8);
+ *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
+ | superio_inb(SUPERIO_REG_BASE_LSB);
+
+ printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC "
+ "(base address 0x%04x, revision %u)\n", *addr, rev);
+
+ superio_exit();
+ return 0;
+}
+
+static int __init smsc47b397_init(void)
+{
+ int ret;
+
+ if ((ret = smsc47b397_find(normal_isa)))
+ return ret;
+
+ return i2c_add_driver(&smsc47b397_driver);
+}
+
+static void __exit smsc47b397_exit(void)
+{
+ i2c_del_driver(&smsc47b397_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <[email protected]>"); +MODULE_DESCRIPTION("SMSC LPC47B397 driver"); +MODULE_LICENSE("GPL");
+
+module_init(smsc47b397_init);
+module_exit(smsc47b397_exit);
Index: linux-2.6.10-mmh/include/linux/i2c-id.h
=================================--- linux-2.6.10-mmh.orig/include/linux/i2c-id.h 2004-12-26 19:38:39.000000000 -0500
+++ linux-2.6.10-mmh/include/linux/i2c-id.h 2004-12-26 20:48:26.000000000 -0500
@@ -166,6 +166,7 @@
#define I2C_DRIVERID_ASB100 1043
#define I2C_DRIVERID_FSCHER 1046
#define I2C_DRIVERID_W83L785TS 1047
+#define I2C_DRIVERID_SMSC47B397 1050
/*
* ---- Adapter types ----------------------------------------------------
Signature · 2 lines
Mark M. Hoffman [email protected]