public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [BK PATCH] I2C update for 2.6.8-rc1
@ 2004-07-15  0:05 Greg KH
  2004-07-15  0:07 ` [PATCH] " Greg KH
                   ` (2 more replies)
  0 siblings, 3 replies; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:05 UTC (permalink / raw)
  To: torvalds, akpm; +Cc: linux-kernel, sensors

Hi,

Here are some i2c driver fixes and updates for 2.6.8-rc1.  There are a
few new i2c chip drivers, and the biggest chunk is the new w1 (1-wire)
driver subsystem contributed by Evgeniy Polyakov.  The sysfs interface
for w1 isn't finished quite yet (just need to create some more sysfs
files, nothing major), but the main functionality is there, and this
allows more w1 drivers to be contributed easier.

Please pull from:  bk://kernel.bkbits.net/gregkh/linux/i2c-2.6

Individual patches will follow, sent to the sensors and linux-kernel
lists.

thanks,

greg k-h

 Documentation/i2c/i2c-pport      |   45 -
 Documentation/i2c/i2c-velleman   |   20 
 Documentation/i2c/i2c-parport    |  156 +++++
 MAINTAINERS                      |   12 
 drivers/Kconfig                  |    2 
 drivers/Makefile                 |    1 
 drivers/i2c/busses/i2c-elektor.c |    6 
 drivers/i2c/busses/i2c-ibm_iic.c |  132 ++++-
 drivers/i2c/busses/scx200_acb.c  |    1 
 drivers/i2c/chips/Kconfig        |   42 +
 drivers/i2c/chips/Makefile       |    3 
 drivers/i2c/chips/adm1025.c      |  570 +++++++++++++++++++++
 drivers/i2c/chips/adm1031.c      | 1021 ++++++++++++++++++++++++++++++++++++++-
 drivers/i2c/chips/lm75.c         |   37 +
 drivers/i2c/chips/lm77.c         |  413 +++++++++++++++
 drivers/i2c/chips/lm78.c         |   88 ---
 drivers/i2c/chips/lm90.c         |   69 +-
 drivers/i2c/i2c-dev.c            |   33 -
 drivers/pci/quirks.c             |   58 +-
 drivers/w1/Kconfig               |   31 +
 drivers/w1/Makefile              |    9 
 drivers/w1/matrox_w1.c           |  246 +++++++++
 drivers/w1/w1.c                  |  623 +++++++++++++++++++++++
 drivers/w1/w1.h                  |  112 ++++
 drivers/w1/w1_family.c           |  133 +++++
 drivers/w1/w1_family.h           |   65 ++
 drivers/w1/w1_int.c              |  207 +++++++
 drivers/w1/w1_int.h              |   36 +
 drivers/w1/w1_io.c               |  138 +++++
 drivers/w1/w1_io.h               |   35 +
 drivers/w1/w1_log.h              |   38 +
 drivers/w1/w1_netlink.c          |   55 ++
 drivers/w1/w1_netlink.h          |   44 +
 drivers/w1/w1_therm.c            |  177 ++++++
 34 files changed, 4411 insertions(+), 247 deletions(-)
-----

<alex:alexdalton.org>:
  o I2C: small ADM1030 fix
  o I2C: ADM1030 and Co sensors chips support

<drewie:freemail.hu>:
  o I2C: Add support for LM77

<orange:fobie.net>:
  o I2C: patch quirks.c - SMBus hidden on hp laptop

Eugene Surovegin:
  o I2C PPC4xx IIC driver: 0-length transactions bit-banging implementation

Greg Kroah-Hartman:
  o 1 Wire: add Dallas 1-wire protocol driver subsystem
  o I2C: sparse cleanups for a few i2c drivers

Jean Delvare:
  o I2C: Refine detection of LM75 chips
  o I2C: adm1025 driver ported to 2.6
  o I2C: remove Documentation for i2c-pport
  o I2C: Documentation for i2c-parport
  o I2C: Add support for LM86, MAX6657 and MAX6658 to lm90
  o I2C: Class of scx200_acb

Luiz Capitulino:
  o I2C: i2c/i2c-dev.c::i2c_dev_init() cleanup

Mark M. Hoffman:
  o I2C: Remove extra inits from lm78 driver


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:05 [BK PATCH] I2C update for 2.6.8-rc1 Greg KH
@ 2004-07-15  0:07 ` Greg KH
  2004-07-15  0:07   ` Greg KH
  2004-07-17 14:30 ` Greg (or anyone else) one small i2c question Reinder
  2004-08-24 21:58 ` [BK PATCH] I2C update for 2.6.8-rc1 Alex Williamson
  2 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.1, 2004/07/08 16:05:54-07:00, alex@alexdalton.org

[PATCH] I2C: ADM1030 and Co sensors chips support

Here is a patch against linux-2.6.7-mm1 kernel that adds support for
adm1030 and adm1031 sensors chips.

It has been reviewed several times by Jean Delvare, and he thinks it is
ready for inclusion into 2.6 kernel. So here it is.


Signed-off-by: Alexandre d'Alton <alex@alexdalton.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/Kconfig   |   10 
 drivers/i2c/chips/Makefile  |    1 
 drivers/i2c/chips/adm1031.c |  988 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 999 insertions(+)


diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig	2004-07-14 17:00:47 -07:00
+++ b/drivers/i2c/chips/Kconfig	2004-07-14 17:00:47 -07:00
@@ -22,6 +22,16 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called adm1021.
 
+config SENSORS_ADM1031
+	tristate "Analog Devices ADM1031 and compatibles"
+	depends on I2C && EXPERIMENTAL
+	select I2C_SENSOR
+	help
+	  If you say yes here you get support for Analog Devices ADM1031 
+	  and ADM1030 sensor chips.
+	  This driver can also be built as a module.  If so, the module
+	  will be called adm1031.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on I2C && EXPERIMENTAL
diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
--- a/drivers/i2c/chips/Makefile	2004-07-14 17:00:47 -07:00
+++ b/drivers/i2c/chips/Makefile	2004-07-14 17:00:47 -07:00
@@ -8,6 +8,7 @@
 obj-$(CONFIG_SENSORS_W83781D)	+= w83781d.o
 
 obj-$(CONFIG_SENSORS_ADM1021)	+= adm1021.o
+obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_EEPROM)	+= eeprom.o
 obj-$(CONFIG_SENSORS_FSCHER)	+= fscher.o
diff -Nru a/drivers/i2c/chips/adm1031.c b/drivers/i2c/chips/adm1031.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/chips/adm1031.c	2004-07-14 17:00:47 -07:00
@@ -0,0 +1,988 @@
+/*
+  adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
+  monitoring
+  Based on lm75.c and lm85.c
+  Supports adm1030 / adm1031
+  Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
+  Reworked by Jean Delvare <khali@linux-fr.org>
+  
+  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/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/* Following macros takes channel parameter starting from 0 to 2 */
+#define ADM1031_REG_FAN_SPEED(nr)	(0x08 + (nr))
+#define ADM1031_REG_FAN_DIV(nr)		(0x20  + (nr))
+#define ADM1031_REG_PWM			(0x22)
+#define ADM1031_REG_FAN_MIN(nr)		(0x10 + (nr))
+
+#define ADM1031_REG_TEMP_MAX(nr)	(0x14  + 4*(nr))
+#define ADM1031_REG_TEMP_MIN(nr)	(0x15  + 4*(nr))
+#define ADM1031_REG_TEMP_CRIT(nr)	(0x16  + 4*(nr))
+
+#define ADM1031_REG_TEMP(nr)		(0xa + (nr))
+#define ADM1031_REG_AUTO_TEMP(nr)	(0x24 + (nr))
+
+#define ADM1031_REG_STATUS(nr)		(0x2 + (nr))
+
+#define ADM1031_REG_CONF1		0x0
+#define ADM1031_REG_CONF2		0x1
+#define ADM1031_REG_EXT_TEMP		0x6
+
+#define ADM1031_CONF1_MONITOR_ENABLE	0x01	/* Monitoring enable */
+#define ADM1031_CONF1_PWM_INVERT	0x08	/* PWM Invert */
+#define ADM1031_CONF1_AUTO_MODE		0x80	/* Auto FAN */
+
+#define ADM1031_CONF2_PWM1_ENABLE	0x01
+#define ADM1031_CONF2_PWM2_ENABLE	0x02
+#define ADM1031_CONF2_TACH1_ENABLE	0x04
+#define ADM1031_CONF2_TACH2_ENABLE	0x08
+#define ADM1031_CONF2_TEMP_ENABLE(chan)	(0x10 << (chan))
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_2(adm1030, adm1031);
+
+typedef u8 auto_chan_table_t[8][2];
+
+/* Each client has this additional data */
+struct adm1031_data {
+	struct i2c_client client;
+	struct semaphore update_lock;
+	int chip_type;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	/* The chan_select_table contains the possible configurations for
+	 * auto fan control.
+	 */
+	auto_chan_table_t *chan_select_table;
+	u16 alarm;
+	u8 conf1;
+	u8 conf2;
+	u8 fan[2];
+	u8 fan_div[2];
+	u8 fan_min[2];
+	u8 pwm[2];
+	u8 old_pwm[2];
+	s8 temp[3];
+	u8 ext_temp[3];
+	u8 auto_temp[3];
+	u8 auto_temp_min[3];
+	u8 auto_temp_off[3];
+	u8 auto_temp_max[3];
+	s8 temp_min[3];
+	s8 temp_max[3];
+	s8 temp_crit[3];
+};
+
+static int adm1031_attach_adapter(struct i2c_adapter *adapter);
+static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind);
+static void adm1031_init_client(struct i2c_client *client);
+static int adm1031_detach_client(struct i2c_client *client);
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg);
+static inline int
+adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value);
+
+static struct adm1031_data *adm1031_update_device(struct device *dev);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver adm1031_driver = {
+	.owner = THIS_MODULE,
+	.name = "adm1031",
+	.flags = I2C_DF_NOTIFY,
+	.attach_adapter = adm1031_attach_adapter,
+	.detach_client = adm1031_detach_client,
+};
+
+static int adm1031_id = 0;
+
+#define TEMP_TO_REG(val)		(((val) < 0 ? ((val - 500) / 1000) : \
+					((val + 500) / 1000)))
+
+#define TEMP_FROM_REG(val)		((val) * 1000)
+
+#define TEMP_FROM_REG_EXT(val, ext)	(TEMP_FROM_REG(val) + (ext) * 125)
+
+#define FAN_FROM_REG(reg, div)		((reg) ? (11250 * 60) / ((reg) * (div)) : 0)
+
+static int FAN_TO_REG(int reg, int div)
+{
+	int tmp;
+	tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div);
+	return tmp > 255 ? 255 : tmp;
+}
+
+#define FAN_DIV_FROM_REG(reg)		(1<<(((reg)&0xc0)>>6))
+
+#define PWM_TO_REG(val)			(SENSORS_LIMIT((val), 0, 255) >> 4)
+#define PWM_FROM_REG(val)		((val) << 4)
+
+#define FAN_CHAN_FROM_REG(reg)		(((reg) >> 5) & 7)
+#define FAN_CHAN_TO_REG(val, reg)	\
+	(((reg) & 0x1F) | (((val) << 5) & 0xe0))
+
+#define AUTO_TEMP_MIN_TO_REG(val, reg)	\
+	((((val)/500) & 0xf8)|((reg) & 0x7))
+#define AUTO_TEMP_RANGE_FROM_REG(reg)	(5000 * (1<< ((reg)&0x7)))
+#define AUTO_TEMP_MIN_FROM_REG(reg)	(1000 * ((((reg) >> 3) & 0x1f) << 2))
+
+#define AUTO_TEMP_MIN_FROM_REG_DEG(reg)	((((reg) >> 3) & 0x1f) << 2)
+
+#define AUTO_TEMP_OFF_FROM_REG(reg)		\
+	(AUTO_TEMP_MIN_FROM_REG(reg) - 5000)
+
+#define AUTO_TEMP_MAX_FROM_REG(reg)		\
+	AUTO_TEMP_RANGE_FROM_REG(reg) +		\
+	AUTO_TEMP_MIN_FROM_REG(reg)
+
+static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
+{
+	int ret;
+	int range = val - AUTO_TEMP_MIN_FROM_REG(reg);
+
+	range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm);
+	ret = ((reg & 0xf8) |
+	       (range < 10000 ? 0 :
+		range < 20000 ? 1 :
+		range < 40000 ? 2 : range < 80000 ? 3 : 4));
+	return ret;
+}
+
+/* FAN auto control */
+#define GET_FAN_AUTO_BITFIELD(data, idx)	\
+	(*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2]
+
+/* The tables below contains the possible values for the auto fan 
+ * control bitfields. the index in the table is the register value.
+ * MSb is the auto fan control enable bit, so the four first entries
+ * in the table disables auto fan control when both bitfields are zero.
+ */
+static auto_chan_table_t auto_channel_select_table_adm1031 = {
+	{0, 0}, {0, 0}, {0, 0}, {0, 0},
+	{2 /*0b010 */ , 4 /*0b100 */ },
+	{2 /*0b010 */ , 2 /*0b010 */ },
+	{4 /*0b100 */ , 4 /*0b100 */ },
+	{7 /*0b111 */ , 7 /*0b111 */ },
+};
+
+static auto_chan_table_t auto_channel_select_table_adm1030 = {
+	{0, 0}, {0, 0}, {0, 0}, {0, 0},
+	{2 /*0b10 */		, 0},
+	{0xff /*invalid */	, 0},
+	{0xff /*invalid */	, 0},
+	{3 /*0b11 */		, 0},
+};
+
+/* That function checks if a bitfield is valid and returns the other bitfield
+ * nearest match if no exact match where found.
+ */
+static int
+get_fan_auto_nearest(struct adm1031_data *data,
+		     int chan, u8 val, u8 reg, u8 * new_reg)
+{
+	int i;
+	int first_match = -1, exact_match = -1;
+	u8 other_reg_val =
+	    (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1];
+
+	if (val == 0) {
+		*new_reg = 0;
+		return 0;
+	}
+
+	for (i = 0; i < 8; i++) {
+		if ((val == (*data->chan_select_table)[i][chan]) &&
+		    ((*data->chan_select_table)[i][chan ? 0 : 1] ==
+		     other_reg_val)) {
+			/* We found an exact match */
+			exact_match = i;
+			break;
+		} else if (val == (*data->chan_select_table)[i][chan] &&
+			   first_match == -1) {
+			/* Save the first match in case of an exact match has not been
+			 * found 
+			 */
+			first_match = i;
+		}
+	}
+
+	if (exact_match >= 0) {
+		*new_reg = exact_match;
+	} else if (first_match >= 0) {
+		*new_reg = first_match;
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr));
+}
+
+static ssize_t
+set_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+	u8 reg;
+	int ret;
+	u8 old_fan_mode;
+
+	old_fan_mode = data->conf1;
+
+	down(&data->update_lock);
+	val = simple_strtol(buf, NULL, 10);
+	
+	if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, &reg))) {
+		up(&data->update_lock);
+		return ret;
+	}
+	if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^ 
+	    (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {
+		if (data->conf1 & ADM1031_CONF1_AUTO_MODE){
+			/* Switch to Auto Fan Mode 
+			 * Save PWM registers 
+			 * Set PWM registers to 33% Both */
+			data->old_pwm[0] = data->pwm[0];
+			data->old_pwm[1] = data->pwm[1];
+			adm1031_write_value(client, ADM1031_REG_PWM, 0x55);
+		} else {
+			/* Switch to Manual Mode */
+			data->pwm[0] = data->old_pwm[0];
+			data->pwm[1] = data->old_pwm[1];
+			/* Restore PWM registers */
+			adm1031_write_value(client, ADM1031_REG_PWM, 
+					    data->pwm[0] | (data->pwm[1] << 4));
+		}
+	}
+	data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
+	adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1);
+	up(&data->update_lock);
+	return count;
+}
+
+#define fan_auto_channel_offset(offset)						\
+static ssize_t show_fan_auto_channel_##offset (struct device *dev, char *buf)	\
+{										\
+	return show_fan_auto_channel(dev, buf, 0x##offset - 1);			\
+}										\
+static ssize_t set_fan_auto_channel_##offset (struct device *dev,		\
+	const char *buf, size_t count)						\
+{										\
+	return set_fan_auto_channel(dev, buf, count, 0x##offset - 1);		\
+}										\
+static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR,		\
+		   show_fan_auto_channel_##offset,				\
+		   set_fan_auto_channel_##offset)
+
+fan_auto_channel_offset(1);
+fan_auto_channel_offset(2);
+
+/* Auto Temps */
+static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", 
+		       AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n",
+		       AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t
+set_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+
+	down(&data->update_lock);
+	val = simple_strtol(buf, NULL, 10);
+	data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);
+	adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
+			    data->auto_temp[nr]);
+	up(&data->update_lock);
+	return count;
+}
+static ssize_t show_auto_temp_max(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n",
+		       AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t
+set_auto_temp_max(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+
+	down(&data->update_lock);
+	val = simple_strtol(buf, NULL, 10);
+	data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]);
+	adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
+			    data->temp_max[nr]);
+	up(&data->update_lock);
+	return count;
+}
+
+#define auto_temp_reg(offset)							\
+static ssize_t show_auto_temp_##offset##_off (struct device *dev, char *buf)	\
+{										\
+	return show_auto_temp_off(dev, buf, 0x##offset - 1);			\
+}										\
+static ssize_t show_auto_temp_##offset##_min (struct device *dev, char *buf)	\
+{										\
+	return show_auto_temp_min(dev, buf, 0x##offset - 1);			\
+}										\
+static ssize_t show_auto_temp_##offset##_max (struct device *dev, char *buf)	\
+{										\
+	return show_auto_temp_max(dev, buf, 0x##offset - 1);			\
+}										\
+static ssize_t set_auto_temp_##offset##_min (struct device *dev,		\
+					     const char *buf, size_t count)	\
+{										\
+	return set_auto_temp_min(dev, buf, count, 0x##offset - 1);		\
+}										\
+static ssize_t set_auto_temp_##offset##_max (struct device *dev,		\
+					     const char *buf, size_t count)	\
+{										\
+	return set_auto_temp_max(dev, buf, count, 0x##offset - 1);		\
+}										\
+static DEVICE_ATTR(auto_temp##offset##_off, S_IRUGO,				\
+		   show_auto_temp_##offset##_off, NULL);			\
+static DEVICE_ATTR(auto_temp##offset##_min, S_IRUGO | S_IWUSR,			\
+		   show_auto_temp_##offset##_min, set_auto_temp_##offset##_min);\
+static DEVICE_ATTR(auto_temp##offset##_max, S_IRUGO | S_IWUSR,			\
+		   show_auto_temp_##offset##_max, set_auto_temp_##offset##_max)
+
+auto_temp_reg(1);
+auto_temp_reg(2);
+auto_temp_reg(3);
+
+/* pwm */
+static ssize_t show_pwm(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
+}
+static ssize_t
+set_pwm(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+	int reg;
+	down(&data->update_lock);
+	val = simple_strtol(buf, NULL, 10);
+	if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) && 
+	    (((val>>4) & 0xf) != 5)) {
+		/* In automatic mode, the only PWM accepted is 33% */
+		up(&data->update_lock);
+		return -EINVAL;
+	}
+	data->pwm[nr] = PWM_TO_REG(val);
+	reg = adm1031_read_value(client, ADM1031_REG_PWM);
+	adm1031_write_value(client, ADM1031_REG_PWM,
+			    nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf)
+			    : (data->pwm[nr] & 0xf) | (reg & 0xf0));
+	up(&data->update_lock);
+	return count;
+}
+
+#define pwm_reg(offset)							\
+static ssize_t show_pwm_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_pwm(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_pwm_##offset (struct device *dev,			\
+				 const char *buf, size_t count)		\
+{									\
+	return set_pwm(dev, buf, count, 0x##offset - 1);		\
+}									\
+static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR,		\
+		   show_pwm_##offset, set_pwm_##offset)
+
+pwm_reg(1);
+pwm_reg(2);
+
+/* Fans */
+
+/*
+ * That function checks the cases where the fan reading is not
+ * relevent.  It is used to provide 0 as fan reading when the fan is
+ * not supposed to run
+ */
+static int trust_fan_readings(struct adm1031_data *data, int chan)
+{
+	int res = 0;
+
+	if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
+		switch (data->conf1 & 0x60) {
+		case 0x00:	/* remote temp1 controls fan1 remote temp2 controls fan2 */
+			res = data->temp[chan+1] >=
+			      AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
+			break;
+		case 0x20:	/* remote temp1 controls both fans */
+			res =
+			    data->temp[1] >=
+			    AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]);
+			break;
+		case 0x40:	/* remote temp2 controls both fans */
+			res =
+			    data->temp[2] >=
+			    AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]);
+			break;
+		case 0x60:	/* max controls both fans */
+			res =
+			    data->temp[0] >=
+			    AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0])
+			    || data->temp[1] >=
+			    AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1])
+			    || (data->chip_type == adm1031 
+				&& data->temp[2] >=
+				AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]));
+			break;
+		}
+	} else {
+		res = data->pwm[chan] > 0;
+	}
+	return res;
+}
+
+
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	int value;
+
+	value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr],
+				 FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
+	return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr]));
+}
+static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n",
+		       FAN_FROM_REG(data->fan_min[nr],
+				    FAN_DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t
+set_fan_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+
+	down(&data->update_lock);
+	val = simple_strtol(buf, NULL, 10);
+	if (val) {
+		data->fan_min[nr] = 
+			FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr]));
+	} else {
+		data->fan_min[nr] = 0xff;
+	}
+	adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]);
+	up(&data->update_lock);
+	return count;
+}
+static ssize_t
+set_fan_div(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+	u8 tmp;
+	int old_div = FAN_DIV_FROM_REG(data->fan_div[nr]);
+	int new_min;
+
+	val = simple_strtol(buf, NULL, 10);
+	tmp = val == 8 ? 0xc0 :
+	      val == 4 ? 0x80 :
+	      val == 2 ? 0x40 :	
+	      val == 1 ? 0x00 :  
+	      0xff;
+	if (tmp == 0xff)
+		return -EINVAL;
+	down(&data->update_lock);
+	data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]);
+	new_min = data->fan_min[nr] * old_div / 
+		FAN_DIV_FROM_REG(data->fan_div[nr]);
+	data->fan_min[nr] = new_min > 0xff ? 0xff : new_min;
+	data->fan[nr] = data->fan[nr] * old_div / 
+		FAN_DIV_FROM_REG(data->fan_div[nr]);
+
+	adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr), 
+			    data->fan_div[nr]);
+	adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), 
+			    data->fan_min[nr]);
+	up(&data->update_lock);
+	return count;
+}
+
+#define fan_offset(offset)						\
+static ssize_t show_fan_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_fan(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t show_fan_##offset##_min (struct device *dev, char *buf)	\
+{									\
+	return show_fan_min(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t show_fan_##offset##_div (struct device *dev, char *buf)	\
+{									\
+	return show_fan_div(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_fan_##offset##_min (struct device *dev,		\
+	const char *buf, size_t count)					\
+{									\
+	return set_fan_min(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t set_fan_##offset##_div (struct device *dev,		\
+	const char *buf, size_t count)					\
+{									\
+	return set_fan_div(dev, buf, count, 0x##offset - 1);		\
+}									\
+static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset,	\
+		   NULL);						\
+static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,		\
+		   show_fan_##offset##_min, set_fan_##offset##_min);	\
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,		\
+		   show_fan_##offset##_div, set_fan_##offset##_div);	\
+static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR,	\
+		   show_pwm_##offset, set_pwm_##offset)
+
+fan_offset(1);
+fan_offset(2);
+
+
+/* Temps */
+static ssize_t show_temp(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	int ext;
+	ext = nr == 0 ?
+	    ((data->ext_temp[nr] >> 6) & 0x3) * 2 :
+	    (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
+	return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));
+}
+static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+static ssize_t show_temp_crit(struct device *dev, char *buf, int nr)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
+}
+static ssize_t
+set_temp_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+
+	val = simple_strtol(buf, NULL, 10);
+	val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
+	down(&data->update_lock);
+	data->temp_min[nr] = TEMP_TO_REG(val);
+	adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr),
+			    data->temp_min[nr]);
+	up(&data->update_lock);
+	return count;
+}
+static ssize_t
+set_temp_max(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+
+	val = simple_strtol(buf, NULL, 10);
+	val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
+	down(&data->update_lock);
+	data->temp_max[nr] = TEMP_TO_REG(val);
+	adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr),
+			    data->temp_max[nr]);
+	up(&data->update_lock);
+	return count;
+}
+static ssize_t
+set_temp_crit(struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int val;
+
+	val = simple_strtol(buf, NULL, 10);
+	val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
+	down(&data->update_lock);
+	data->temp_crit[nr] = TEMP_TO_REG(val);
+	adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr),
+			    data->temp_crit[nr]);
+	up(&data->update_lock);
+	return count;
+}
+
+#define temp_reg(offset)							\
+static ssize_t show_temp_##offset (struct device *dev, char *buf)		\
+{										\
+	return show_temp(dev, buf, 0x##offset - 1);				\
+}										\
+static ssize_t show_temp_##offset##_min (struct device *dev, char *buf)		\
+{										\
+	return show_temp_min(dev, buf, 0x##offset - 1);				\
+}										\
+static ssize_t show_temp_##offset##_max (struct device *dev, char *buf)		\
+{										\
+	return show_temp_max(dev, buf, 0x##offset - 1);				\
+}										\
+static ssize_t show_temp_##offset##_crit (struct device *dev, char *buf)	\
+{										\
+	return show_temp_crit(dev, buf, 0x##offset - 1);			\
+}										\
+static ssize_t set_temp_##offset##_min (struct device *dev,			\
+					const char *buf, size_t count)		\
+{										\
+	return set_temp_min(dev, buf, count, 0x##offset - 1);			\
+}										\
+static ssize_t set_temp_##offset##_max (struct device *dev,			\
+					const char *buf, size_t count)		\
+{										\
+	return set_temp_max(dev, buf, count, 0x##offset - 1);			\
+}										\
+static ssize_t set_temp_##offset##_crit (struct device *dev,			\
+					 const char *buf, size_t count)		\
+{										\
+	return set_temp_crit(dev, buf, count, 0x##offset - 1);			\
+}										\
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset,		\
+		   NULL);							\
+static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR,			\
+		   show_temp_##offset##_min, set_temp_##offset##_min);		\
+static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR,			\
+		   show_temp_##offset##_max, set_temp_##offset##_max);		\
+static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR,			\
+		   show_temp_##offset##_crit, set_temp_##offset##_crit)
+
+temp_reg(1);
+temp_reg(2);
+temp_reg(3);
+
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+	struct adm1031_data *data = adm1031_update_device(dev);
+	return sprintf(buf, "%d\n", data->alarm);
+}
+
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+
+static int adm1031_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!(adapter->class & I2C_CLASS_HWMON))
+		return 0;
+	return i2c_detect(adapter, &addr_data, adm1031_detect);
+}
+
+/* This function is called by i2c_detect */
+static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client;
+	struct adm1031_data *data;
+	int err = 0;
+	const char *name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto exit;
+
+	if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset(data, 0, sizeof(struct adm1031_data));
+
+	new_client = &data->client;
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1031_driver;
+	new_client->flags = 0;
+
+	if (kind < 0) {
+		int id, co;
+		id = i2c_smbus_read_byte_data(new_client, 0x3d);
+		co = i2c_smbus_read_byte_data(new_client, 0x3e);
+
+		if (!((id == 0x31 || id == 0x30) && co == 0x41))
+			goto exit_free;
+		kind = (id == 0x30) ? adm1030 : adm1031;
+	}
+
+	if (kind <= 0)
+		kind = adm1031;
+
+	/* Given the detected chip type, set the chip name and the
+	 * auto fan control helper table. */
+	if (kind == adm1030) {
+		name = "adm1030";
+		data->chan_select_table = &auto_channel_select_table_adm1030;
+	} else if (kind == adm1031) {
+		name = "adm1031";
+		data->chan_select_table = &auto_channel_select_table_adm1031;
+	}
+	data->chip_type = kind;
+
+	strlcpy(new_client->name, name, I2C_NAME_SIZE);
+
+	new_client->id = adm1031_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto exit_free;
+
+	/* Initialize the ADM1031 chip */
+	adm1031_init_client(new_client);
+
+	/* Register sysfs hooks */
+	device_create_file(&new_client->dev, &dev_attr_fan1_input);
+	device_create_file(&new_client->dev, &dev_attr_fan1_div);
+	device_create_file(&new_client->dev, &dev_attr_fan1_min);
+	device_create_file(&new_client->dev, &dev_attr_fan1_pwm);
+	device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel);
+	device_create_file(&new_client->dev, &dev_attr_temp1_input);
+	device_create_file(&new_client->dev, &dev_attr_temp1_min);
+	device_create_file(&new_client->dev, &dev_attr_temp1_max);
+	device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+	device_create_file(&new_client->dev, &dev_attr_temp2_input);
+	device_create_file(&new_client->dev, &dev_attr_temp2_min);
+	device_create_file(&new_client->dev, &dev_attr_temp2_max);
+	device_create_file(&new_client->dev, &dev_attr_temp2_crit);
+
+	device_create_file(&new_client->dev, &dev_attr_auto_temp1_off);
+	device_create_file(&new_client->dev, &dev_attr_auto_temp1_min);
+	device_create_file(&new_client->dev, &dev_attr_auto_temp1_max);
+
+	device_create_file(&new_client->dev, &dev_attr_auto_temp2_off);
+	device_create_file(&new_client->dev, &dev_attr_auto_temp2_min);
+	device_create_file(&new_client->dev, &dev_attr_auto_temp2_max);
+
+	device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm);
+
+	device_create_file(&new_client->dev, &dev_attr_alarms);
+
+	if (kind == adm1031) {
+		device_create_file(&new_client->dev, &dev_attr_fan2_input);
+		device_create_file(&new_client->dev, &dev_attr_fan2_div);
+		device_create_file(&new_client->dev, &dev_attr_fan2_min);
+		device_create_file(&new_client->dev, &dev_attr_fan2_pwm);
+		device_create_file(&new_client->dev,
+				   &dev_attr_auto_fan2_channel);
+		device_create_file(&new_client->dev, &dev_attr_temp3_input);
+		device_create_file(&new_client->dev, &dev_attr_temp3_min);
+		device_create_file(&new_client->dev, &dev_attr_temp3_max);
+		device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+		device_create_file(&new_client->dev, &dev_attr_auto_temp3_off);
+		device_create_file(&new_client->dev, &dev_attr_auto_temp3_min);
+		device_create_file(&new_client->dev, &dev_attr_auto_temp3_max);
+		device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm);
+	}
+
+	return 0;
+
+exit_free:
+	kfree(new_client);
+exit:
+	return err;
+}
+
+static int adm1031_detach_client(struct i2c_client *client)
+{
+	int ret;
+	if ((ret = i2c_detach_client(client)) != 0) {
+		return ret;
+	}
+	kfree(client);
+	return 0;
+}
+
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int
+adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void adm1031_init_client(struct i2c_client *client)
+{
+	unsigned int read_val;
+	unsigned int mask;
+	struct adm1031_data *data = i2c_get_clientdata(client);
+
+	mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
+	if (data->chip_type == adm1031) {
+		mask |= (ADM1031_CONF2_PWM2_ENABLE |
+			ADM1031_CONF2_TACH2_ENABLE);
+	} 
+	/* Initialize the ADM1031 chip (enables fan speed reading ) */
+	read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
+	if ((read_val | mask) != read_val) {
+	    adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
+	}
+
+	read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
+	if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
+	    adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
+				ADM1031_CONF1_MONITOR_ENABLE);
+	}
+
+}
+
+static struct adm1031_data *adm1031_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1031_data *data = i2c_get_clientdata(client);
+	int chan;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+		dev_dbg(&client->dev, "Starting adm1031 update\n");
+		for (chan = 0;
+		     chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) {
+			u8 oldh, newh;
+
+			oldh =
+			    adm1031_read_value(client, ADM1031_REG_TEMP(chan));
+			data->ext_temp[chan] =
+			    adm1031_read_value(client, ADM1031_REG_EXT_TEMP);
+			newh =
+			    adm1031_read_value(client, ADM1031_REG_TEMP(chan));
+			if (newh != oldh) {
+				data->ext_temp[chan] =
+				    adm1031_read_value(client,
+						       ADM1031_REG_EXT_TEMP);
+#ifdef DEBUG
+				oldh =
+				    adm1031_read_value(client,
+						       ADM1031_REG_TEMP(chan));
+
+				/* oldh is actually newer */
+				if (newh != oldh)
+					dev_warn(&client->dev,
+						 "Remote temperature may be "
+						 "wrong.\n");
+#endif
+			}
+			data->temp[chan] = newh;
+
+			data->temp_min[chan] =
+			    adm1031_read_value(client,
+					       ADM1031_REG_TEMP_MIN(chan));
+			data->temp_max[chan] =
+			    adm1031_read_value(client,
+					       ADM1031_REG_TEMP_MAX(chan));
+			data->temp_crit[chan] =
+			    adm1031_read_value(client,
+					       ADM1031_REG_TEMP_CRIT(chan));
+			data->auto_temp[chan] =
+			    adm1031_read_value(client,
+					       ADM1031_REG_AUTO_TEMP(chan));
+
+		}
+
+		data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
+		data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
+
+		data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0))
+			     | (adm1031_read_value(client, ADM1031_REG_STATUS(1))
+				<< 8);
+		if (data->chip_type == adm1030) {
+			data->alarm &= 0xc0ff;
+		}
+		
+		for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) {
+			data->fan_div[chan] =
+			    adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan));
+			data->fan_min[chan] =
+			    adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan));
+			data->fan[chan] =
+			    adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan));
+			data->pwm[chan] =
+			    0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >> 
+				   (4*chan));
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+
+	return data;
+}
+
+static int __init sensors_adm1031_init(void)
+{
+	return i2c_add_driver(&adm1031_driver);
+}
+
+static void __exit sensors_adm1031_exit(void)
+{
+	i2c_del_driver(&adm1031_driver);
+}
+
+MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
+MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_adm1031_init);
+module_exit(sensors_adm1031_exit);


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07 ` [PATCH] " Greg KH
@ 2004-07-15  0:07   ` Greg KH
  2004-07-15  0:07     ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.2, 2004/07/08 16:06:22-07:00, ebs@ebshome.net

[PATCH] I2C PPC4xx IIC driver: 0-length transactions bit-banging implementation

IBM PPC 4xx i2c controller doesn't support 0-length transactions (e.g. used by
SMBUS_QUICK). This patch implements bit-banging emulation for such requests and
removes temporary kludge added earlier.

Signed-off-by: Eugene Surovegin <ebs@ebshome.net>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/busses/i2c-ibm_iic.c |  132 +++++++++++++++++++++++++++++++++++++--
 1 files changed, 126 insertions(+), 6 deletions(-)


diff -Nru a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
--- a/drivers/i2c/busses/i2c-ibm_iic.c	2004-07-14 17:00:40 -07:00
+++ b/drivers/i2c/busses/i2c-ibm_iic.c	2004-07-14 17:00:40 -07:00
@@ -45,7 +45,7 @@
 
 #include "i2c-ibm_iic.h"
 
-#define DRIVER_VERSION "2.01"
+#define DRIVER_VERSION "2.1"
 
 MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION);
 MODULE_LICENSE("GPL");
@@ -96,6 +96,31 @@
 #  define DUMP_REGS(h,dev)	((void)0)
 #endif
 
+/* Bus timings (in ns) for bit-banging */
+static struct i2c_timings {
+	unsigned int hd_sta;
+	unsigned int su_sto;
+	unsigned int low;
+	unsigned int high;
+	unsigned int buf;
+} timings [] = {
+/* Standard mode (100 KHz) */
+{
+	.hd_sta	= 4000,
+	.su_sto	= 4000,
+	.low	= 4700,
+	.high	= 4000,
+	.buf	= 4700,
+},
+/* Fast mode (400 KHz) */
+{
+	.hd_sta = 600,
+	.su_sto	= 600,
+	.low 	= 1300,
+	.high 	= 600,
+	.buf	= 1300,
+}};
+
 /* Enable/disable interrupt generation */
 static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable)
 {
@@ -196,6 +221,104 @@
 }
 
 /*
+ * Do 0-length transaction using bit-banging through IIC_DIRECTCNTL register.
+ */
+
+/* Wait for SCL and/or SDA to be high */
+static int iic_dc_wait(volatile struct iic_regs *iic, u8 mask)
+{
+	unsigned long x = jiffies + HZ / 28 + 2;
+	while ((in_8(&iic->directcntl) & mask) != mask){
+		if (unlikely(time_after(jiffies, x)))
+			return -1;
+		cond_resched();
+	}
+	return 0;
+}
+
+static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
+{
+	volatile struct iic_regs* iic = dev->vaddr;
+	const struct i2c_timings* t = &timings[dev->fast_mode ? 1 : 0];
+	u8 mask, v, sda;
+	int i, res;
+
+	/* Only 7-bit addresses are supported */
+	if (unlikely(p->flags & I2C_M_TEN)){
+		DBG("%d: smbus_quick - 10 bit addresses are not supported\n",
+			dev->idx);
+		return -EINVAL;
+	}
+
+	DBG("%d: smbus_quick(0x%02x)\n", dev->idx, p->addr);
+
+	/* Reset IIC interface */
+	out_8(&iic->xtcntlss, XTCNTLSS_SRST);
+
+	/* Wait for bus to become free */
+	out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
+	if (unlikely(iic_dc_wait(iic, DIRCNTL_MSDA | DIRCNTL_MSC)))
+		goto err;
+	ndelay(t->buf);
+
+	/* START */
+	out_8(&iic->directcntl, DIRCNTL_SCC);
+	sda = 0;
+	ndelay(t->hd_sta);
+
+	/* Send address */
+	v = (u8)((p->addr << 1) | ((p->flags & I2C_M_RD) ? 1 : 0));
+	for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){
+		out_8(&iic->directcntl, sda);
+		ndelay(t->low / 2);
+		sda = (v & mask) ? DIRCNTL_SDAC : 0;
+		out_8(&iic->directcntl, sda);
+		ndelay(t->low / 2);
+
+		out_8(&iic->directcntl, DIRCNTL_SCC | sda);
+		if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
+			goto err;
+		ndelay(t->high);
+	}
+
+	/* ACK */
+	out_8(&iic->directcntl, sda);
+	ndelay(t->low / 2);
+	out_8(&iic->directcntl, DIRCNTL_SDAC);
+	ndelay(t->low / 2);
+	out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
+	if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
+		goto err;
+	res = (in_8(&iic->directcntl) & DIRCNTL_MSDA) ? -EREMOTEIO : 1;
+	ndelay(t->high);
+
+	/* STOP */
+	out_8(&iic->directcntl, 0);
+	ndelay(t->low);
+	out_8(&iic->directcntl, DIRCNTL_SCC);
+	if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
+		goto err;
+	ndelay(t->su_sto);
+	out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
+
+	ndelay(t->buf);
+
+	DBG("%d: smbus_quick -> %s\n", dev->idx, res ? "NACK" : "ACK");
+out:
+	/* Remove reset */
+	out_8(&iic->xtcntlss, 0);
+
+	/* Reinitialize interface */
+	iic_dev_init(dev);
+
+	return res;
+err:
+	DBG("%d: smbus_quick - bus is stuck\n", dev->idx);
+	res = -EREMOTEIO;
+	goto out;
+}
+
+/*
  * IIC interrupt handler
  */
 static irqreturn_t iic_handler(int irq, void *dev_id, struct pt_regs *regs)
@@ -457,13 +580,10 @@
 		if (unlikely(msgs[i].len <= 0)){
 			if (num == 1 && !msgs[0].len){
 				/* Special case for I2C_SMBUS_QUICK emulation.
-				 * Although this logic is FAR FROM PERFECT, this 
-				 * is what previous driver version did.
 				 * IBM IIC doesn't support 0-length transactions
-				 * (except bit-banging through IICx_DIRECTCNTL).
+				 * so we have to emulate them using bit-banging.
 				 */
-				DBG("%d: zero-length msg kludge\n", dev->idx); 
-				return 0;
+				return iic_smbus_quick(dev, &msgs[0]);
 			}
 			DBG("%d: invalid len %d in msg[%d]\n", dev->idx, 
 				msgs[i].len, i);


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07     ` Greg KH
@ 2004-07-15  0:07       ` Greg KH
  2004-07-15  0:07         ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.4, 2004/07/08 16:07:05-07:00, khali@linux-fr.org

[PATCH] I2C: Add support for LM86, MAX6657 and MAX6658 to lm90

This adds support for the LM86, MAX6657 and MAX6658 sensor chips to the
lm90 driver. These are less popular than the LM90 and ADM1032 but
several users have reported to use these, so I added support to the lm90
driver. All these chips are fully compatible so that's just a matter of
accepting the new chip ids. I also slightly simplified the detection
code.


Signed-off-by: Jean Delvare <khali at linux-fr dot org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/Kconfig |    3 +-
 drivers/i2c/chips/lm90.c  |   69 ++++++++++++++++++++++++++++++----------------
 2 files changed, 48 insertions(+), 24 deletions(-)


diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig	2004-07-14 17:00:18 -07:00
+++ b/drivers/i2c/chips/Kconfig	2004-07-14 17:00:18 -07:00
@@ -150,7 +150,8 @@
 	select I2C_SENSOR
 	help
 	  If you say yes here you get support for National Semiconductor LM90,
-	  LM89 and LM99, and Analog Devices ADM1032 sensor chips.
+	  LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and
+	  MAX6658 sensor chips.
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm90.
diff -Nru a/drivers/i2c/chips/lm90.c b/drivers/i2c/chips/lm90.c
--- a/drivers/i2c/chips/lm90.c	2004-07-14 17:00:18 -07:00
+++ b/drivers/i2c/chips/lm90.c	2004-07-14 17:00:18 -07:00
@@ -21,11 +21,26 @@
  *   http://www.national.com/pf/LM/LM99.html
  * Note that there is no way to differenciate between both chips.
  *
+ * This driver also supports the LM86, another sensor chip made by
+ * National Semiconductor. It is exactly similar to the LM90 except it
+ * has a higher accuracy.
+ * Complete datasheet can be obtained from National's website at:
+ *   http://www.national.com/pf/LM/LM86.html
+ *
  * This driver also supports the ADM1032, a sensor chip made by Analog
  * Devices. That chip is similar to the LM90, with a few differences
  * that are not handled by this driver. Complete datasheet can be
  * obtained from Analog's website at:
  *   http://products.analog.com/products/info.asp?product=ADM1032
+ * Among others, it has a higher accuracy than the LM90, much like the
+ * LM86 does.
+ *
+ * This driver also supports the MAX6657 and MAX6658, sensor chips made
+ * by Maxim. These chips are similar to the LM86. Complete datasheet
+ * can be obtained at Maxim's website at:
+ *   http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
+ * Note that there is no way to differenciate between both chips (but
+ * no need either).
  *
  * Since the LM90 was the first chipset supported by this driver, most
  * comments will refer to this chipset, but are actually general and
@@ -56,7 +71,7 @@
 /*
  * Addresses to scan
  * Address is fully defined internally and cannot be changed.
- * LM89, LM90, LM99 and ADM1032 have address 0x4c.
+ * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c.
  * LM89-1, and LM99-1 have address 0x4d.
  */
 
@@ -69,7 +84,7 @@
  * Insmod parameters
  */
 
-SENSORS_INSMOD_3(lm90, adm1032, lm99);
+SENSORS_INSMOD_5(lm90, adm1032, lm99, lm86, max6657);
 
 /*
  * The LM90 registers
@@ -289,7 +304,6 @@
 	struct lm90_data *data;
 	int err = 0;
 	const char *name = "";
-	u8 reg_config1=0, reg_convrate=0;
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 		goto exit;
@@ -319,28 +333,22 @@
 	 * requested, so both the detection and the identification steps
 	 * are skipped.
 	 */
-	if (kind < 0) { /* detection */
-		reg_config1 = i2c_smbus_read_byte_data(new_client,
-			      LM90_REG_R_CONFIG1);
-		reg_convrate = i2c_smbus_read_byte_data(new_client,
-			       LM90_REG_R_CONVRATE);
 
-		if ((reg_config1 & 0x2A) != 0x00
-		 || reg_convrate > 0x0A) {
-			dev_dbg(&adapter->dev,
-				"LM90 detection failed at 0x%02x.\n",
-				address);
-			goto exit_free;
-		}
-	}
+	/* Default to an LM90 if forced */
+	if (kind == 0)
+		kind = lm90;
 
-	if (kind <= 0) { /* identification */
-		u8 man_id, chip_id;
+	if (kind < 0) { /* detection and identification */
+		u8 man_id, chip_id, reg_config1, reg_convrate;
 
 		man_id = i2c_smbus_read_byte_data(new_client,
 			 LM90_REG_R_MAN_ID);
 		chip_id = i2c_smbus_read_byte_data(new_client,
 			  LM90_REG_R_CHIP_ID);
+		reg_config1 = i2c_smbus_read_byte_data(new_client,
+			      LM90_REG_R_CONFIG1);
+		reg_convrate = i2c_smbus_read_byte_data(new_client,
+			       LM90_REG_R_CONVRATE);
 		
 		if (man_id == 0x01) { /* National Semiconductor */
 			u8 reg_config2;
@@ -348,25 +356,36 @@
 			reg_config2 = i2c_smbus_read_byte_data(new_client,
 				      LM90_REG_R_CONFIG2);
 
-			if (kind == 0 /* skip detection */
-			 || ((reg_config2 & 0xF8) == 0x00
-			  && reg_convrate <= 0x09)) {
+			if ((reg_config1 & 0x2A) == 0x00
+			 && (reg_config2 & 0xF8) == 0x00
+			 && reg_convrate <= 0x09) {
 				if (address == 0x4C
 				 && (chip_id & 0xF0) == 0x20) { /* LM90 */
 					kind = lm90;
 				} else
 				if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
 					kind = lm99;
+				} else
+				if (address == 0x4C
+				 && (chip_id & 0xF0) == 0x10) { /* LM86 */
+					kind = lm86;
 				}
 			}
 		} else
 		if (man_id == 0x41) { /* Analog Devices */
 			if (address == 0x4C
 			 && (chip_id & 0xF0) == 0x40 /* ADM1032 */
-			 && (kind == 0 /* skip detection */
-			  || (reg_config1 & 0x3F) == 0x00)) {
+			 && (reg_config1 & 0x3F) == 0x00
+			 && reg_convrate <= 0x0A) {
 				kind = adm1032;
 			}
+		} else
+		if (man_id == 0x4D) { /* Maxim */
+			if (address == 0x4C
+			 && (reg_config1 & 0x1F) == 0
+			 && reg_convrate <= 0x09) {
+			 	kind = max6657;
+			}
 		}
 
 		if (kind <= 0) { /* identification failed */
@@ -383,6 +402,10 @@
 		name = "adm1032";
 	} else if (kind == lm99) {
 		name = "lm99";
+	} else if (kind == lm86) {
+		name = "lm86";
+	} else if (kind == max6657) {
+		name = "max6657";
 	}
 
 	/* We can fill in the remaining client fields */


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07   ` Greg KH
@ 2004-07-15  0:07     ` Greg KH
  2004-07-15  0:07       ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.3, 2004/07/08 16:06:41-07:00, khali@linux-fr.org

[PATCH] I2C: Class of scx200_acb

This is needed for the scx200_acb to accept hardware monitoring chips.

Signed-off-by: Jean Delvare <khali at linux-fr dot org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/busses/scx200_acb.c |    1 +
 1 files changed, 1 insertion(+)


diff -Nru a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
--- a/drivers/i2c/busses/scx200_acb.c	2004-07-14 17:00:29 -07:00
+++ b/drivers/i2c/busses/scx200_acb.c	2004-07-14 17:00:29 -07:00
@@ -458,6 +458,7 @@
 	adapter->owner = THIS_MODULE;
 	adapter->id = I2C_ALGO_SMBUS;
 	adapter->algo = &scx200_acb_algorithm;
+	adapter->class = I2C_CLASS_HWMON;
 
 	init_MUTEX(&iface->sem);
 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07       ` Greg KH
@ 2004-07-15  0:07         ` Greg KH
  2004-07-15  0:07           ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.5, 2004/07/08 16:07:27-07:00, lcapitulino@prefeitura.sp.gov.br

[PATCH] I2C: i2c/i2c-dev.c::i2c_dev_init() cleanup.

 This patch does the fallowing cleanup for
drivers/i2c/i2c-dev.c::i2c_dev_init():

*) in a error condition, return the error code of register_chrdev()
insted of -EIO;
*) adds missing audit for class_register();
*) in a error condition, only prints "Driver Initialisation Failed",
insted printing the cause. (Note that the error will be printed by
the return of the error code, and the information about what function
caused the problem need to be done by a debug code).

 Only compiled, lack of hardware.

Signed-off-by: Luiz Capitulino <lcapitulino@prefeitura.sp.gov.br>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/i2c-dev.c |   33 +++++++++++++++++++++------------
 1 files changed, 21 insertions(+), 12 deletions(-)


diff -Nru a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
--- a/drivers/i2c/i2c-dev.c	2004-07-14 17:00:12 -07:00
+++ b/drivers/i2c/i2c-dev.c	2004-07-14 17:00:12 -07:00
@@ -518,20 +518,29 @@
 
 	printk(KERN_INFO "i2c /dev entries driver\n");
 
-	if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) {
-		printk(KERN_ERR "i2c-dev.o: unable to get major %d for i2c bus\n",
-		       I2C_MAJOR);
-		return -EIO;
-	}
+	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
+	if (res)
+		goto out;
+
+	res = class_register(&i2c_dev_class);
+	if (res)
+		goto out_unreg_chrdev;
+
+	res = i2c_add_driver(&i2cdev_driver);
+	if (res)
+		goto out_unreg_class;
+
 	devfs_mk_dir("i2c");
-	class_register(&i2c_dev_class);
-	if ((res = i2c_add_driver(&i2cdev_driver))) {
-		printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n");
-		devfs_remove("i2c");
-		unregister_chrdev(I2C_MAJOR,"i2c");
-		return res;
-	}
+
 	return 0;
+
+out_unreg_class:
+	class_unregister(&i2c_dev_class);
+out_unreg_chrdev:
+	unregister_chrdev(I2C_MAJOR, "i2c");
+out:
+	printk(KERN_ERR "%s: Driver Initialisation failed", __FILE__);
+	return res;
 }
 
 static void __exit i2c_dev_exit(void)


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07         ` Greg KH
@ 2004-07-15  0:07           ` Greg KH
  2004-07-15  0:07             ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.6, 2004/07/08 16:07:58-07:00, khali@linux-fr.org

[PATCH] I2C: Documentation for i2c-parport

At least, the i2c-parport gets some documentation. I heard several
persons complaining that there was no sample electronics schema for
building their own i2c-over-parallel-port, so I did just that, with the
help of Sylvain Munaut. The documentation also includes the list of
supported adapters, and a short comparison with other drivers using the
parallel port to drive an i2c bus. At the end of the document I included
an updated version of the i2c-velleman doc file (which I then deleted).

Signed-off-by: Jean Delvare <khali at linux-fr dot org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 Documentation/i2c/i2c-velleman |   20 -----
 Documentation/i2c/i2c-parport  |  156 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 156 insertions(+), 20 deletions(-)


diff -Nru a/Documentation/i2c/i2c-parport b/Documentation/i2c/i2c-parport
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/Documentation/i2c/i2c-parport	2004-07-14 17:00:03 -07:00
@@ -0,0 +1,156 @@
+==================
+i2c-parport driver
+==================
+
+2004-07-06, Jean Delvare
+
+This is a unified driver for several i2c-over-parallel-port adapters,
+such as the ones made by Philips, Velleman or ELV. This driver is
+meant as a replacement for the older, individual drivers:
+ * i2c-philips-par
+ * i2c-elv
+ * i2c-velleman
+ * video/i2c-parport (NOT the same as this one, dedicated to home brew
+                      teletext adapters)
+
+It currently supports the following devices:
+ * Philips adapter
+ * home brew teletext adapter
+ * Velleman K8000 adapter
+ * ELV adapter
+ * Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032)
+
+These devices use different pinout configurations, so you have to tell
+the driver what you have, using the type module parameter. There is no
+way to autodetect the devices. Support for different pinout configurations
+can be easily added when needed.
+
+
+Building your own adapter
+-------------------------
+
+If you want to build you own i2c-over-parallel-port adapter, here is
+a sample electronics schema (credits go to Sylvain Munaut):
+
+Device                                                      PC
+Side          ___________________Vdd (+)                    Side
+               |    |         |
+              ---  ---       ---
+              | |  | |       | |
+              |R|  |R|       |R|
+              | |  | |       | |
+              ---  ---       ---
+               |    |         |
+               |    |    /|   |
+SCL  ----------x--------o |-----------x-------------------  pin 2
+                    |    \|   |       |
+                    |         |       |
+                    |   |\    |       |
+SDA  ----------x----x---| o---x---------------------------  pin 13
+               |        |/            |
+               |                      |
+               |         /|           |
+               ---------o |----------------x--------------  pin 3
+                         \|           |    |
+                                      |    |
+                                     ---  ---
+                                     | |  | |
+                                     |R|  |R|
+                                     | |  | |
+                                     ---  ---
+                                      |    | 
+                                     ###  ###
+                                     GND  GND
+        
+Remarks:
+ - This is the exact pinout and electronics used on the Analog Devices
+   evaluation boards.
+                   /|
+ - All inverters -o |- must be 74HC05, they must be open collector output.
+                   \|
+ - All resitors are 10k.
+ - Pins 18-25 of the parallel port connected to GND.
+ - Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high.
+   The ADM1032 evaluation board uses D4-D7. Beware that the amount of
+   current you can draw from the parallel port is limited. Also note that
+   all connected lines MUST BE driven at the same state, else you'll short
+   circuit the output buffers! So plugging the I2C adapter after loading
+   the i2c-parport module might be a good safety since data line state
+   prior to init may be unknown. 
+ - This is 5V!
+ - Obviously you cannot read SCL (so it's not really standard-compliant).
+   Pretty easy to add, just copy the SDA part and use another input pin.
+   That would give (ELV compatible pinout):
+
+
+Device                                                      PC
+Side          ______________________________Vdd (+)         Side
+               |    |            |    |
+              ---  ---          ---  ---
+              | |  | |          | |  | |
+              |R|  |R|          |R|  |R|
+              | |  | |          | |  | |
+              ---  ---          ---  ---
+               |    |            |    |
+               |    |      |\    |    |
+SCL  ----------x--------x--| o---x------------------------  pin 15
+                    |   |  |/         | 
+                    |   |             |
+                    |   |   /|        |
+                    |   ---o |-------------x--------------  pin 2
+                    |       \|        |    |
+                    |                 |    |
+                    |                 |    |
+                    |      |\         |    |
+SDA  ---------------x---x--| o--------x-------------------  pin 10
+                        |  |/              |
+                        |                  |
+                        |   /|             |
+                        ---o |------------------x---------  pin 3
+                            \|             |    |
+                                           |    |
+                                          ---  ---
+                                          | |  | |
+                                          |R|  |R|
+                                          | |  | |
+                                          ---  ---
+                                           |    | 
+                                          ###  ###
+                                          GND  GND
+
+
+If possible, you should use the same pinout configuration as existing
+adapters do, so you won't even have to change the code.
+
+
+Similar (but different) drivers
+-------------------------------
+
+This driver is NOT the same as the i2c-pport driver found in the i2c package.
+The i2c-pport driver makes use of modern parallel port features so that
+you don't need additional electronics. It has other restrictions however, and
+was not ported to Linux 2.6 (yet).
+
+This driver is also NOT the same as the i2c-pcf-epp driver found in the
+lm_sensors package. The i2c-pcf-epp driver doesn't use the parallel port
+as an I2C bus directly. Instead, it uses it to control an external I2C bus
+master. That driver was not ported to Linux 2.6 (yet) either.
+
+
+Legacy documentation for Velleman adapter
+-----------------------------------------
+
+Useful links:
+Velleman                http://www.velleman.be/
+Velleman K8000 Howto    http://howto.htlw16.ac.at/k8000-howto.html
+
+The project has lead to new libs for the Velleman K8000 and K8005:
+  LIBK8000 v1.99.1 and LIBK8005 v0.21
+With these libs, you can control the K8000 interface card and the K8005
+stepper motor card with the simple commands which are in the original
+Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and
+many more, using /dev/velleman.
+  http://home.wanadoo.nl/hihihi/libk8000.htm
+  http://home.wanadoo.nl/hihihi/libk8005.htm
+  http://struyve.mine.nu:8080/index.php?block=k8000
+  http://sourceforge.net/projects/libk8005/
diff -Nru a/Documentation/i2c/i2c-velleman b/Documentation/i2c/i2c-velleman
--- a/Documentation/i2c/i2c-velleman	2004-07-14 17:00:03 -07:00
+++ /dev/null	Wed Dec 31 16:00:00 196900
@@ -1,23 +0,0 @@
-i2c-velleman driver
--------------------
-This is a driver for i2c-hw access for Velleman K8000 and other adapters.
-
-Useful links
-------------
-Velleman:
-	http://www.velleman.be/
-
-Velleman K8000 Howto:
-	http://howto.htlw16.ac.at/k8000-howto.html
-
-K8000 and K8005 libraries
--------------------------
-The project has lead to new libs for the Velleman K8000 and K8005:
-LIBK8000 v1.99.1 and LIBK8005 v0.21
-
-With these libs, you can control the K8000 interface card and the K8005
-stepper motor card with the simple commands which are in the original
-Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and
-many more, using /dev/velleman.
-
-The libs can be found on http://groups.yahoo.com/group/k8000/files/linux/


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07           ` Greg KH
@ 2004-07-15  0:07             ` Greg KH
  2004-07-15  0:07               ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.7, 2004/07/08 16:08:19-07:00, drewie@freemail.hu

[PATCH] I2C: Add support for LM77

This patch (against the current stack) adds support for the
LM77 sensor chips made by National Semiconductor. Formerly
this was claimed by the LM75 driver but when I got hold of an
embedded board (built around the National Geode SC1100 CPU),
which was equipped with an LM77, it turned out that the two
chips are not compatible.

It has been developed with help of, reviewed and approved
by Jean Delvare.


Signed-off-by: Andras Bali <drewie@freemail.hu>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/Kconfig  |   11 +
 drivers/i2c/chips/Makefile |    1 
 drivers/i2c/chips/lm77.c   |  413 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 425 insertions(+)


diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig	2004-07-14 16:59:52 -07:00
+++ b/drivers/i2c/chips/Kconfig	2004-07-14 16:59:52 -07:00
@@ -99,6 +99,17 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm75.
 
+config SENSORS_LM77
+	tristate "National Semiconductor LM77"
+	depends on I2C && EXPERIMENTAL
+	select I2C_SENSOR
+	help
+	  If you say yes here you get support for National Semiconductor LM77
+	  sensor chips.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called lm77.
+
 config SENSORS_LM78
 	tristate "National Semiconductor LM78 and compatibles"
 	depends on I2C && EXPERIMENTAL
diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
--- a/drivers/i2c/chips/Makefile	2004-07-14 16:59:52 -07:00
+++ b/drivers/i2c/chips/Makefile	2004-07-14 16:59:52 -07:00
@@ -15,6 +15,7 @@
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_LM75)	+= lm75.o
+obj-$(CONFIG_SENSORS_LM77)	+= lm77.o
 obj-$(CONFIG_SENSORS_LM78)	+= lm78.o
 obj-$(CONFIG_SENSORS_LM80)	+= lm80.o
 obj-$(CONFIG_SENSORS_LM83)	+= lm83.o
diff -Nru a/drivers/i2c/chips/lm77.c b/drivers/i2c/chips/lm77.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/chips/lm77.c	2004-07-14 16:59:52 -07:00
@@ -0,0 +1,413 @@
+/*
+    lm77.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+
+    Copyright (c) 2004  Andras BALI <drewie@freemail.hu>
+
+    Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>.  The LM77
+    is a temperature sensor and thermal window comparator with 0.5 deg
+    resolution made by National Semiconductor.  Complete datasheet can be
+    obtained at their site:
+       http://www.national.com/pf/LM/LM77.html
+
+    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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4b, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm77);
+
+/* The LM77 registers */
+#define LM77_REG_TEMP		0x00
+#define LM77_REG_CONF		0x01
+#define LM77_REG_TEMP_HYST	0x02
+#define LM77_REG_TEMP_CRIT	0x03
+#define LM77_REG_TEMP_MIN	0x04
+#define LM77_REG_TEMP_MAX	0x05
+
+/* Each client has this additional data */
+struct lm77_data {
+	struct i2c_client	client;
+	struct semaphore	update_lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+	int			temp_input;	/* Temperatures */
+	int			temp_crit;
+	int			temp_min;
+	int			temp_max;
+	int			temp_hyst;
+	u8			alarms;
+};
+
+static int lm77_attach_adapter(struct i2c_adapter *adapter);
+static int lm77_detect(struct i2c_adapter *adapter, int address, int kind);
+static void lm77_init_client(struct i2c_client *client);
+static int lm77_detach_client(struct i2c_client *client);
+static u16 lm77_read_value(struct i2c_client *client, u8 reg);
+static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value);
+
+static struct lm77_data *lm77_update_device(struct device *dev);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm77_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "lm77",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter = lm77_attach_adapter,
+	.detach_client	= lm77_detach_client,
+};
+
+static int lm77_id = 0;
+
+/* straight from the datasheet */
+#define LM77_TEMP_MIN (-55000)
+#define LM77_TEMP_MAX 125000
+
+/* In the temperature registers, the low 3 bits are not part of the
+   temperature values; they are the status bits. */
+static inline u16 LM77_TEMP_TO_REG(int temp)
+{
+	int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX);
+	return (u16)((ntemp / 500) * 8);
+}
+
+static inline int LM77_TEMP_FROM_REG(u16 reg)
+{
+	return ((int)reg / 8) * 500;
+}
+
+/* sysfs stuff */
+
+/* read routines for temperature limits */
+#define show(value)	\
+static ssize_t show_##value(struct device *dev, char *buf)	\
+{								\
+	struct lm77_data *data = lm77_update_device(dev);	\
+	return sprintf(buf, "%d\n", data->value);		\
+}
+
+show(temp_input);
+show(temp_crit);
+show(temp_min);
+show(temp_max);
+show(alarms);
+
+/* read routines for hysteresis values */
+static ssize_t show_temp_crit_hyst(struct device *dev, char *buf)
+{
+	struct lm77_data *data = lm77_update_device(dev);
+	return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst);
+}
+static ssize_t show_temp_min_hyst(struct device *dev, char *buf)
+{
+	struct lm77_data *data = lm77_update_device(dev);
+	return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst);
+}
+static ssize_t show_temp_max_hyst(struct device *dev, char *buf)
+{
+	struct lm77_data *data = lm77_update_device(dev);
+	return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst);
+}
+
+/* write routines */
+#define set(value, reg)	\
+static ssize_t set_##value(struct device *dev, const char *buf, size_t count)	\
+{										\
+	struct i2c_client *client = to_i2c_client(dev);				\
+	struct lm77_data *data = i2c_get_clientdata(client);			\
+	data->value = simple_strtoul(buf, NULL, 10);				\
+	lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value));		\
+	return count;								\
+}
+
+set(temp_min, LM77_REG_TEMP_MIN);
+set(temp_max, LM77_REG_TEMP_MAX);
+
+/* hysteresis is stored as a relative value on the chip, so it has to be
+   converted first */
+static ssize_t set_temp_crit_hyst(struct device *dev, const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm77_data *data = i2c_get_clientdata(client);
+	data->temp_hyst = data->temp_crit - simple_strtoul(buf, NULL, 10);
+	lm77_write_value(client, LM77_REG_TEMP_HYST,
+			 LM77_TEMP_TO_REG(data->temp_hyst));
+	return count;
+}
+
+/* preserve hysteresis when setting T_crit */
+static ssize_t set_temp_crit(struct device *dev, const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm77_data *data = i2c_get_clientdata(client);
+	int oldcrithyst = data->temp_crit - data->temp_hyst;
+	data->temp_crit = simple_strtoul(buf, NULL, 10);
+	data->temp_hyst = data->temp_crit - oldcrithyst;
+	lm77_write_value(client, LM77_REG_TEMP_CRIT,
+			 LM77_TEMP_TO_REG(data->temp_crit));
+	lm77_write_value(client, LM77_REG_TEMP_HYST,
+			 LM77_TEMP_TO_REG(data->temp_hyst));
+	return count;
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO,
+		   show_temp_input, NULL);
+static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+		   show_temp_crit, set_temp_crit);
+static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+		   show_temp_min, set_temp_min);
+static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+		   show_temp_max, set_temp_max);
+
+static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
+		   show_temp_crit_hyst, set_temp_crit_hyst);
+static DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
+		   show_temp_min_hyst, NULL);
+static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
+		   show_temp_max_hyst, NULL);
+
+static DEVICE_ATTR(alarms, S_IRUGO,
+		   show_alarms, NULL);
+
+static int lm77_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!(adapter->class & I2C_CLASS_HWMON))
+		return 0;
+	return i2c_detect(adapter, &addr_data, lm77_detect);
+}
+
+/* This function is called by i2c_detect */
+static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client;
+	struct lm77_data *data;
+	int err = 0;
+	const char *name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA))
+		goto exit;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access lm77_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct lm77_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset(data, 0, sizeof(struct lm77_data));
+
+	new_client = &data->client;
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &lm77_driver;
+	new_client->flags = 0;
+
+	/* Here comes the remaining detection.  Since the LM77 has no
+	   register dedicated to identification, we have to rely on the
+	   following tricks:
+
+	   1. the high 4 bits represent the sign and thus they should
+	      always be the same
+	   2. the high 3 bits are unused in the configuration register
+	   3. addresses 0x06 and 0x07 return the last read value
+	   4. registers cycling over 8-address boundaries
+
+	   Word-sized registers are high-byte first. */
+	if (kind < 0) {
+		int i, cur, conf, hyst, crit, min, max;
+
+		/* addresses cycling */
+		cur = i2c_smbus_read_word_data(new_client, 0);
+		conf = i2c_smbus_read_byte_data(new_client, 1);
+		hyst = i2c_smbus_read_word_data(new_client, 2);
+		crit = i2c_smbus_read_word_data(new_client, 3);
+		min = i2c_smbus_read_word_data(new_client, 4);
+		max = i2c_smbus_read_word_data(new_client, 5);
+		for (i = 8; i <= 0xff; i += 8)
+			if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+			    || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+			    || i2c_smbus_read_word_data(new_client, i + 3) != crit
+			    || i2c_smbus_read_word_data(new_client, i + 4) != min
+			    || i2c_smbus_read_word_data(new_client, i + 5) != max)
+				goto exit_free;
+
+		/* sign bits */
+		if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
+		    || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
+		    || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
+		    || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
+		    || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
+			goto exit_free;
+
+		/* unused bits */
+		if (conf & 0xe0)
+			goto exit_free;
+
+		/* 0x06 and 0x07 return the last read value */
+		cur = i2c_smbus_read_word_data(new_client, 0);
+		if (i2c_smbus_read_word_data(new_client, 6) != cur
+		    || i2c_smbus_read_word_data(new_client, 7) != cur)
+			goto exit_free;
+		hyst = i2c_smbus_read_word_data(new_client, 2);
+		if (i2c_smbus_read_word_data(new_client, 6) != hyst
+		    || i2c_smbus_read_word_data(new_client, 7) != hyst)
+			goto exit_free;
+		min = i2c_smbus_read_word_data(new_client, 4);
+		if (i2c_smbus_read_word_data(new_client, 6) != min
+		    || i2c_smbus_read_word_data(new_client, 7) != min)
+			goto exit_free;
+
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = lm77;
+
+	if (kind == lm77) {
+		name = "lm77";
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strlcpy(new_client->name, name, I2C_NAME_SIZE);
+
+	new_client->id = lm77_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto exit_free;
+
+	/* Initialize the LM77 chip */
+	lm77_init_client(new_client);
+
+	/* Register sysfs hooks */
+	device_create_file(&new_client->dev, &dev_attr_temp1_input);
+	device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+	device_create_file(&new_client->dev, &dev_attr_temp1_min);
+	device_create_file(&new_client->dev, &dev_attr_temp1_max);
+	device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_alarms);
+	return 0;
+
+exit_free:
+	kfree(data);
+exit:
+	return err;
+}
+
+static int lm77_detach_client(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+/* All registers are word-sized, except for the configuration register.
+   The LM77 uses the high-byte first convention. */
+static u16 lm77_read_value(struct i2c_client *client, u8 reg)
+{
+	if (reg == LM77_REG_CONF)
+		return i2c_smbus_read_byte_data(client, reg);
+	else
+		return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == LM77_REG_CONF)
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+
+static void lm77_init_client(struct i2c_client *client)
+{
+	/* Initialize the LM77 chip - turn off shutdown mode */
+	int conf = lm77_read_value(client, LM77_REG_CONF);
+	if (conf & 1)
+		lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
+}
+
+static struct lm77_data *lm77_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm77_data *data = i2c_get_clientdata(client);
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		dev_dbg(&client->dev, "Starting lm77 update\n");
+		data->temp_input =
+			LM77_TEMP_FROM_REG(lm77_read_value(client,
+							   LM77_REG_TEMP));
+		data->temp_hyst =
+			LM77_TEMP_FROM_REG(lm77_read_value(client,
+							   LM77_REG_TEMP_HYST));
+		data->temp_crit =
+			LM77_TEMP_FROM_REG(lm77_read_value(client,
+							   LM77_REG_TEMP_CRIT));
+		data->temp_min =
+			LM77_TEMP_FROM_REG(lm77_read_value(client,
+							   LM77_REG_TEMP_MIN));
+		data->temp_max =
+			LM77_TEMP_FROM_REG(lm77_read_value(client,
+							   LM77_REG_TEMP_MAX));
+		data->alarms =
+			lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+
+	return data;
+}
+
+static int __init sensors_lm77_init(void)
+{
+	return i2c_add_driver(&lm77_driver);
+}
+
+static void __exit sensors_lm77_exit(void)
+{
+	i2c_del_driver(&lm77_driver);
+}
+
+MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
+MODULE_DESCRIPTION("LM77 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm77_init);
+module_exit(sensors_lm77_exit);


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07             ` Greg KH
@ 2004-07-15  0:07               ` Greg KH
  2004-07-15  0:07                 ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.8, 2004/07/09 14:47:23-07:00, khali@linux-fr.org

[PATCH] I2C: remove Documentation for i2c-pport

> > This also raises a question about Documentation/i2c/i2c-pport.
> > Should we keep a document about a driver which is not in the kernel
> > tree (and hasn't even been ported to 2.6 yet)?
>
> No we should not.

Signed-off-by: Jean Delvare <khali at linux-fr dot org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 Documentation/i2c/i2c-pport |   45 --------------------------------------------
 1 files changed, 45 deletions(-)


diff -Nru a/Documentation/i2c/i2c-pport b/Documentation/i2c/i2c-pport
--- a/Documentation/i2c/i2c-pport	2004-07-14 16:59:45 -07:00
+++ /dev/null	Wed Dec 31 16:00:00 196900
@@ -1,45 +0,0 @@
-Primitive parallel port is driver for i2c bus, which exploits 
-features of modern bidirectional parallel ports. 
-
-Bidirectional ports have particular bits connected in following way:
-   
-                        |
-            /-----|     R
-         --o|     |-----|
-      read  \-----|     /------- Out pin
-                      |/
-                   - -|\
-                write   V
-                        |
-                       ---  
-
-
-It means when output is set to 1 we can read the port. Therefore 
-we can use 2 pins of parallel port as SDA and SCL for i2c bus. It 
-is not necessary to add any external - additional parts, we can 
-read and write the same port simultaneously.
-	I only use register base+2 so it is possible to use all 
-8 data bits of parallel port for other applications (I have 
-connected EEPROM and LCD display). I do not use bit Enable Bi-directional
- Port. The only disadvantage is we can only support 5V chips.
-
-Layout:
-
-Cannon 25 pin
-
-SDA - connect to pin 14 (Auto Linefeed)
-SCL - connect to pin 16 (Initialize Printer)
-GND - connect to pin 18-25
-+5V - use external supply (I use 5V from 3.5" floppy connector)
-      
-no pullups  requied
-
-Module parameters:
-
-base = 0xXXX
-XXX - 278 or 378
-
-That's all.
-
-Daniel Smolik
-marvin@sitour.cz


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07               ` Greg KH
@ 2004-07-15  0:07                 ` Greg KH
  2004-07-15  0:07                   ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.9, 2004/07/09 14:47:50-07:00, khali@linux-fr.org

[PATCH] I2C: adm1025 driver ported to 2.6

This is my port of the adm1025 driver to 2.6. It has been tested by a
few users and reported to work OK.

Signed-off-by: Jean Delvare <khali at linux-fr dot org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/Kconfig   |   10 
 drivers/i2c/chips/Makefile  |    1 
 drivers/i2c/chips/adm1025.c |  570 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 581 insertions(+)


diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig	2004-07-14 16:59:39 -07:00
+++ b/drivers/i2c/chips/Kconfig	2004-07-14 16:59:39 -07:00
@@ -22,6 +22,16 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called adm1021.
 
+config SENSORS_ADM1025
+	tristate "Analog Devices ADM1025 and compatibles"
+	depends on I2C && EXPERIMENTAL
+	select I2C_SENSOR
+	help
+	  If you say yes here you get support for Analog Devices ADM1025
+	  and Philips NE1619 sensor chips.
+	  This driver can also be built as a module.  If so, the module
+	  will be called adm1025.
+
 config SENSORS_ADM1031
 	tristate "Analog Devices ADM1031 and compatibles"
 	depends on I2C && EXPERIMENTAL
diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
--- a/drivers/i2c/chips/Makefile	2004-07-14 16:59:39 -07:00
+++ b/drivers/i2c/chips/Makefile	2004-07-14 16:59:39 -07:00
@@ -8,6 +8,7 @@
 obj-$(CONFIG_SENSORS_W83781D)	+= w83781d.o
 
 obj-$(CONFIG_SENSORS_ADM1021)	+= adm1021.o
+obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_EEPROM)	+= eeprom.o
diff -Nru a/drivers/i2c/chips/adm1025.c b/drivers/i2c/chips/adm1025.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/chips/adm1025.c	2004-07-14 16:59:39 -07:00
@@ -0,0 +1,570 @@
+/*
+ * adm1025.c
+ *
+ * Copyright (C) 2000       Chen-Yuan Wu <gwu@esoft.com>
+ * Copyright (C) 2003-2004  Jean Delvare <khali@linux-fr.org>
+ *
+ * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
+ * voltages (including its own power source) and up to two temperatures
+ * (its own plus up to one external one). Voltages are scaled internally
+ * (which is not the common way) with ratios such that the nominal value
+ * of each voltage correspond to a register value of 192 (which means a
+ * resolution of about 0.5% of the nominal value). Temperature values are
+ * reported with a 1 deg resolution and a 3 deg accuracy. Complete
+ * datasheet can be obtained from Analog's website at:
+ *   http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html
+ *
+ * This driver also supports the ADM1025A, which differs from the ADM1025
+ * only in that it has "open-drain VID inputs while the ADM1025 has
+ * on-chip 100k pull-ups on the VID inputs". It doesn't make any
+ * difference for us.
+ *
+ * This driver also supports the NE1619, a sensor chip made by Philips.
+ * That chip is similar to the ADM1025A, with a few differences. The only
+ * difference that matters to us is that the NE1619 has only two possible
+ * addresses while the ADM1025A has a third one. Complete datasheet can be
+ * obtained from Philips's website at:
+ *   http://www.semiconductors.philips.com/pip/NE1619DS.html
+ *
+ * Since the ADM1025 was the first chipset supported by this driver, most
+ * comments will refer to this chipset, but are actually general and
+ * concern all supported chipsets, unless mentioned otherwise.
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+
+/*
+ * Addresses to scan
+ * ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e.
+ * NE1619 has two possible addresses: 0x2c and 0x2d.
+ */
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_2(adm1025, ne1619);
+
+/*
+ * The ADM1025 registers
+ */
+
+#define ADM1025_REG_MAN_ID		0x3E
+#define ADM1025_REG_CHIP_ID 		0x3F
+#define ADM1025_REG_CONFIG		0x40
+#define ADM1025_REG_STATUS1		0x41
+#define ADM1025_REG_STATUS2		0x42
+#define ADM1025_REG_IN(nr)		(0x20 + (nr))
+#define ADM1025_REG_IN_MAX(nr)		(0x2B + (nr) * 2)
+#define ADM1025_REG_IN_MIN(nr)		(0x2C + (nr) * 2)
+#define ADM1025_REG_TEMP(nr)		(0x26 + (nr))
+#define ADM1025_REG_TEMP_HIGH(nr)	(0x37 + (nr) * 2)
+#define ADM1025_REG_TEMP_LOW(nr)	(0x38 + (nr) * 2)
+#define ADM1025_REG_VID			0x47
+#define ADM1025_REG_VID4		0x49
+
+/*
+ * Conversions and various macros
+ * The ADM1025 uses signed 8-bit values for temperatures.
+ */
+
+static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
+
+#define IN_FROM_REG(reg,scale)	(((reg) * (scale) + 96) / 192)
+#define IN_TO_REG(val,scale)	((val) <= 0 ? 0 : \
+				 (val) * 192 >= (scale) * 255 ? 255 : \
+				 ((val) * 192 + (scale)/2) / (scale))
+
+#define TEMP_FROM_REG(reg)	((reg) * 1000)
+#define TEMP_TO_REG(val)	((val) <= -127500 ? -128 : \
+				 (val) >= 126500 ? 127 : \
+				 (((val) < 0 ? (val)-500 : (val)+500) / 1000))
+
+/*
+ * Functions declaration
+ */
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter);
+static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind);
+static void adm1025_init_client(struct i2c_client *client);
+static int adm1025_detach_client(struct i2c_client *client);
+static struct adm1025_data *adm1025_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver adm1025_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "adm1025",
+	.id		= I2C_DRIVERID_ADM1025,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= adm1025_attach_adapter,
+	.detach_client	= adm1025_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct adm1025_data {
+	struct i2c_client client;
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	u8 in[6];		/* register value */
+	u8 in_max[6];		/* register value */
+	u8 in_min[6];		/* register value */
+	s8 temp[2];		/* register value */
+	s8 temp_min[2];		/* register value */
+	s8 temp_max[2];		/* register value */
+	u16 alarms;		/* register values, combined */
+	u8 vid;			/* register values, combined */
+	u8 vrm;
+};
+
+/*
+ * Internal variables
+ */
+
+static int adm1025_id = 0;
+
+/*
+ * Sysfs stuff
+ */
+
+#define show_in(offset) \
+static ssize_t show_in##offset(struct device *dev, char *buf) \
+{ \
+	struct adm1025_data *data = adm1025_update_device(dev); \
+	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
+		       in_scale[offset])); \
+} \
+static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
+{ \
+	struct adm1025_data *data = adm1025_update_device(dev); \
+	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
+		       in_scale[offset])); \
+} \
+static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
+{ \
+	struct adm1025_data *data = adm1025_update_device(dev); \
+	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
+		       in_scale[offset])); \
+} \
+static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);
+show_in(0);
+show_in(1);
+show_in(2);
+show_in(3);
+show_in(4);
+show_in(5);
+
+#define show_temp(offset) \
+static ssize_t show_temp##offset(struct device *dev, char *buf) \
+{ \
+	struct adm1025_data *data = adm1025_update_device(dev); \
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
+} \
+static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \
+{ \
+	struct adm1025_data *data = adm1025_update_device(dev); \
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \
+} \
+static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \
+{ \
+	struct adm1025_data *data = adm1025_update_device(dev); \
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \
+}\
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL);
+show_temp(1);
+show_temp(2);
+
+#define set_in(offset) \
+static ssize_t set_in##offset##_min(struct device *dev, const char *buf, \
+	size_t count) \
+{ \
+	struct i2c_client *client = to_i2c_client(dev); \
+	struct adm1025_data *data = i2c_get_clientdata(client); \
+	data->in_min[offset] = IN_TO_REG(simple_strtol(buf, NULL, 10), \
+			       in_scale[offset]); \
+	i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \
+				  data->in_min[offset]); \
+	return count; \
+} \
+static ssize_t set_in##offset##_max(struct device *dev, const char *buf, \
+	size_t count) \
+{ \
+	struct i2c_client *client = to_i2c_client(dev); \
+	struct adm1025_data *data = i2c_get_clientdata(client); \
+	data->in_max[offset] = IN_TO_REG(simple_strtol(buf, NULL, 10), \
+			       in_scale[offset]); \
+	i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \
+				  data->in_max[offset]); \
+	return count; \
+} \
+static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \
+	show_in##offset##_min, set_in##offset##_min); \
+static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \
+	show_in##offset##_max, set_in##offset##_max);
+set_in(0);
+set_in(1);
+set_in(2);
+set_in(3);
+set_in(4);
+set_in(5);
+
+#define set_temp(offset) \
+static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \
+	size_t count) \
+{ \
+	struct i2c_client *client = to_i2c_client(dev); \
+	struct adm1025_data *data = i2c_get_clientdata(client); \
+	data->temp_min[offset-1] = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \
+	i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \
+				  data->temp_min[offset-1]); \
+	return count; \
+} \
+static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \
+	size_t count) \
+{ \
+	struct i2c_client *client = to_i2c_client(dev); \
+	struct adm1025_data *data = i2c_get_clientdata(client); \
+	data->temp_max[offset-1] = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \
+	i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \
+				  data->temp_max[offset-1]); \
+	return count; \
+} \
+static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
+	show_temp##offset##_min, set_temp##offset##_min); \
+static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
+	show_temp##offset##_max, set_temp##offset##_max);
+set_temp(1);
+set_temp(2);
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+	struct adm1025_data *data = adm1025_update_device(dev);
+	return sprintf(buf, "%u\n", data->alarms);
+}
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+static ssize_t show_vid(struct device *dev, char *buf)
+{
+	struct adm1025_data *data = adm1025_update_device(dev);
+	return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, char *buf)
+{
+	struct adm1025_data *data = adm1025_update_device(dev);
+	return sprintf(buf, "%u\n", data->vrm);
+}
+static ssize_t set_vrm(struct device *dev, const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1025_data *data = i2c_get_clientdata(client);
+	data->vrm = simple_strtoul(buf, NULL, 10);
+	return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+/*
+ * Real code
+ */
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!(adapter->class & I2C_CLASS_HWMON))
+		return 0;
+	return i2c_detect(adapter, &addr_data, adm1025_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client;
+	struct adm1025_data *data;
+	int err = 0;
+	const char *name = "";
+	u8 config;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto exit;
+
+	if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset(data, 0, sizeof(struct adm1025_data));
+
+	/* The common I2C client data is placed right before the
+	   ADM1025-specific data. */
+	new_client = &data->client;
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1025_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip. A zero kind means that
+	 * the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+	config = i2c_smbus_read_byte_data(new_client, ADM1025_REG_CONFIG);
+	if (kind < 0) { /* detection */
+		if ((config & 0x80) != 0x00
+		 || (i2c_smbus_read_byte_data(new_client,
+		     ADM1025_REG_STATUS1) & 0xC0) != 0x00
+		 || (i2c_smbus_read_byte_data(new_client,
+		     ADM1025_REG_STATUS2) & 0xBC) != 0x00) {
+			dev_dbg(&adapter->dev,
+				"ADM1025 detection failed at 0x%02x.\n",
+				address);
+			goto exit_free;
+		}
+	}
+
+	if (kind <= 0) { /* identification */
+		u8 man_id, chip_id;
+
+		man_id = i2c_smbus_read_byte_data(new_client,
+			 ADM1025_REG_MAN_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client,
+			  ADM1025_REG_CHIP_ID);
+		
+		if (man_id == 0x41) { /* Analog Devices */
+			if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */
+				kind = adm1025;
+			}
+		} else
+		if (man_id == 0xA1) { /* Philips */
+			if (address != 0x2E
+			 && (chip_id & 0xF0) == 0x20) { /* NE1619 */
+				kind = ne1619;
+			}
+		}
+
+		if (kind <= 0) { /* identification failed */
+			dev_info(&adapter->dev,
+			    "Unsupported chip (man_id=0x%02X, "
+			    "chip_id=0x%02X).\n", man_id, chip_id);
+			goto exit_free;
+		}
+	}
+
+	if (kind == adm1025) {
+		name = "adm1025";
+	} else if (kind == ne1619) {
+		name = "ne1619";
+	}
+
+	/* We can fill in the remaining client fields */
+	strlcpy(new_client->name, name, I2C_NAME_SIZE);
+	new_client->id = adm1025_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto exit_free;
+
+	/* Initialize the ADM1025 chip */
+	adm1025_init_client(new_client);
+
+	/* Register sysfs hooks */
+	device_create_file(&new_client->dev, &dev_attr_in0_input);
+	device_create_file(&new_client->dev, &dev_attr_in1_input);
+	device_create_file(&new_client->dev, &dev_attr_in2_input);
+	device_create_file(&new_client->dev, &dev_attr_in3_input);
+	device_create_file(&new_client->dev, &dev_attr_in5_input);
+	device_create_file(&new_client->dev, &dev_attr_in0_min);
+	device_create_file(&new_client->dev, &dev_attr_in1_min);
+	device_create_file(&new_client->dev, &dev_attr_in2_min);
+	device_create_file(&new_client->dev, &dev_attr_in3_min);
+	device_create_file(&new_client->dev, &dev_attr_in5_min);
+	device_create_file(&new_client->dev, &dev_attr_in0_max);
+	device_create_file(&new_client->dev, &dev_attr_in1_max);
+	device_create_file(&new_client->dev, &dev_attr_in2_max);
+	device_create_file(&new_client->dev, &dev_attr_in3_max);
+	device_create_file(&new_client->dev, &dev_attr_in5_max);
+	device_create_file(&new_client->dev, &dev_attr_temp1_input);
+	device_create_file(&new_client->dev, &dev_attr_temp2_input);
+	device_create_file(&new_client->dev, &dev_attr_temp1_min);
+	device_create_file(&new_client->dev, &dev_attr_temp2_min);
+	device_create_file(&new_client->dev, &dev_attr_temp1_max);
+	device_create_file(&new_client->dev, &dev_attr_temp2_max);
+	device_create_file(&new_client->dev, &dev_attr_alarms);
+	device_create_file(&new_client->dev, &dev_attr_in1_ref);
+	device_create_file(&new_client->dev, &dev_attr_vrm);
+
+	/* Pin 11 is either in4 (+12V) or VID4 */
+	if (!(config & 0x20)) {
+		device_create_file(&new_client->dev, &dev_attr_in4_input);
+		device_create_file(&new_client->dev, &dev_attr_in4_min);
+		device_create_file(&new_client->dev, &dev_attr_in4_max);
+	}
+
+	return 0;
+
+exit_free:
+	kfree(data);
+exit:
+	return err;
+}
+
+static void adm1025_init_client(struct i2c_client *client)
+{
+	u8 reg;
+	struct adm1025_data *data = i2c_get_clientdata(client);
+	int i;
+
+	data->vrm = 82;
+
+	/*
+	 * Set high limits
+	 * Usually we avoid setting limits on driver init, but it happens
+	 * that the ADM1025 comes with stupid default limits (all registers
+	 * set to 0). In case the chip has not gone through any limit
+	 * setting yet, we better set the high limits to the max so that
+	 * no alarm triggers.
+	 */
+	for (i=0; i<6; i++) {
+		reg = i2c_smbus_read_byte_data(client,
+					       ADM1025_REG_IN_MAX(i));
+		if (reg == 0)
+			i2c_smbus_write_byte_data(client,
+						  ADM1025_REG_IN_MAX(i),
+						  0xFF);
+	}
+	for (i=0; i<2; i++) {
+		reg = i2c_smbus_read_byte_data(client,
+					       ADM1025_REG_TEMP_HIGH(i));
+		if (reg == 0)
+			i2c_smbus_write_byte_data(client,
+						  ADM1025_REG_TEMP_HIGH(i),
+						  0x7F);
+	}
+
+	/*
+	 * Start the conversions
+	 */
+	reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
+	if (!(reg & 0x01))
+		i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG,
+					  (reg&0x7E)|0x01);
+}
+
+static int adm1025_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;
+	}
+
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static struct adm1025_data *adm1025_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adm1025_data *data = i2c_get_clientdata(client);
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ * 2) ||
+	    (jiffies < data->last_updated) ||
+	    !data->valid) {
+		int i;
+
+		dev_dbg(&client->dev, "Updating data.\n");
+		for (i=0; i<6; i++) {
+			data->in[i] = i2c_smbus_read_byte_data(client,
+				      ADM1025_REG_IN(i));
+			data->in_min[i] = i2c_smbus_read_byte_data(client,
+					  ADM1025_REG_IN_MIN(i));
+			data->in_max[i] = i2c_smbus_read_byte_data(client,
+					  ADM1025_REG_IN_MAX(i));
+		}
+		for (i=0; i<2; i++) {
+			data->temp[i] = i2c_smbus_read_byte_data(client,
+					ADM1025_REG_TEMP(i));
+			data->temp_min[i] = i2c_smbus_read_byte_data(client,
+					    ADM1025_REG_TEMP_LOW(i));
+			data->temp_max[i] = i2c_smbus_read_byte_data(client,
+					    ADM1025_REG_TEMP_HIGH(i));
+		}
+		data->alarms = i2c_smbus_read_byte_data(client,
+			       ADM1025_REG_STATUS1)
+			     | (i2c_smbus_read_byte_data(client,
+				ADM1025_REG_STATUS2) << 8);
+		data->vid = (i2c_smbus_read_byte_data(client,
+			     ADM1025_REG_VID) & 0x0f)
+			  | ((i2c_smbus_read_byte_data(client,
+			      ADM1025_REG_VID4) & 0x01) << 4);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+
+	return data;
+}
+
+static int __init sensors_adm1025_init(void)
+{
+	return i2c_add_driver(&adm1025_driver);
+}
+
+static void __exit sensors_adm1025_exit(void)
+{
+	i2c_del_driver(&adm1025_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("ADM1025 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_adm1025_init);
+module_exit(sensors_adm1025_exit);


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                 ` Greg KH
@ 2004-07-15  0:07                   ` Greg KH
  2004-07-15  0:07                     ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.10, 2004/07/09 15:07:03-07:00, greg@kroah.com

I2C: sparse cleanups for a few i2c drivers.


 drivers/i2c/busses/i2c-elektor.c |    6 +++---
 drivers/i2c/chips/adm1031.c      |   29 +++++++++++++----------------
 2 files changed, 16 insertions(+), 19 deletions(-)


diff -Nru a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
--- a/drivers/i2c/busses/i2c-elektor.c	2004-07-14 16:59:33 -07:00
+++ b/drivers/i2c/busses/i2c-elektor.c	2004-07-14 16:59:33 -07:00
@@ -143,7 +143,7 @@
 		}
 	}
 	if (irq > 0) {
-		if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) {
+		if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", NULL) < 0) {
 			printk(KERN_ERR "i2c-elektor: Request irq%d failed\n", irq);
 			irq = 0;
 		} else
@@ -244,7 +244,7 @@
  fail:
 	if (irq > 0) {
 		disable_irq(irq);
-		free_irq(irq, 0);
+		free_irq(irq, NULL);
 	}
 
 	if (!mmapped)
@@ -258,7 +258,7 @@
 
 	if (irq > 0) {
 		disable_irq(irq);
-		free_irq(irq, 0);
+		free_irq(irq, NULL);
 	}
 
 	if (!mmapped)
diff -Nru a/drivers/i2c/chips/adm1031.c b/drivers/i2c/chips/adm1031.c
--- a/drivers/i2c/chips/adm1031.c	2004-07-14 16:59:33 -07:00
+++ b/drivers/i2c/chips/adm1031.c	2004-07-14 16:59:33 -07:00
@@ -101,10 +101,6 @@
 static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind);
 static void adm1031_init_client(struct i2c_client *client);
 static int adm1031_detach_client(struct i2c_client *client);
-static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg);
-static inline int
-adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value);
-
 static struct adm1031_data *adm1031_update_device(struct device *dev);
 
 /* This is the driver that will be inserted */
@@ -116,7 +112,19 @@
 	.detach_client = adm1031_detach_client,
 };
 
-static int adm1031_id = 0;
+static int adm1031_id;
+
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int
+adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
 
 #define TEMP_TO_REG(val)		(((val) < 0 ? ((val - 500) / 1000) : \
 					((val + 500) / 1000)))
@@ -847,17 +855,6 @@
 	}
 	kfree(client);
 	return 0;
-}
-
-static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
-{
-	return i2c_smbus_read_byte_data(client, reg);
-}
-
-static inline int
-adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
-{
-	return i2c_smbus_write_byte_data(client, reg, value);
 }
 
 static void adm1031_init_client(struct i2c_client *client)


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                   ` Greg KH
@ 2004-07-15  0:07                     ` Greg KH
  2004-07-15  0:07                       ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.11, 2004/07/09 15:08:41-07:00, alex@alexdalton.org

[PATCH] I2C: small ADM1030 fix

Please find an incremental patch that applies on top of the previous one
(the one from the first message of the thread) and that adds parenthesis
to the macro as pointed out by Mark.


Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/adm1031.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)


diff -Nru a/drivers/i2c/chips/adm1031.c b/drivers/i2c/chips/adm1031.c
--- a/drivers/i2c/chips/adm1031.c	2004-07-14 16:59:22 -07:00
+++ b/drivers/i2c/chips/adm1031.c	2004-07-14 16:59:22 -07:00
@@ -162,8 +162,8 @@
 	(AUTO_TEMP_MIN_FROM_REG(reg) - 5000)
 
 #define AUTO_TEMP_MAX_FROM_REG(reg)		\
-	AUTO_TEMP_RANGE_FROM_REG(reg) +		\
-	AUTO_TEMP_MIN_FROM_REG(reg)
+	(AUTO_TEMP_RANGE_FROM_REG(reg) +	\
+	AUTO_TEMP_MIN_FROM_REG(reg))
 
 static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
 {


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                     ` Greg KH
@ 2004-07-15  0:07                       ` Greg KH
  2004-07-15  0:07                         ` Greg KH
  2004-07-16 17:07                         ` Pavel Machek
  0 siblings, 2 replies; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.12, 2004/07/09 16:39:56-07:00, greg@kroah.com

1 Wire: add Dallas 1-wire protocol driver subsystem

This was written and developed by Evgeniy Polyakov <johnpol@2ka.mipt.ru>
with only very minor cleanups by me.

Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 MAINTAINERS             |    6 
 drivers/Kconfig         |    2 
 drivers/Makefile        |    1 
 drivers/w1/Kconfig      |   31 ++
 drivers/w1/Makefile     |    9 
 drivers/w1/matrox_w1.c  |  246 ++++++++++++++++++
 drivers/w1/w1.c         |  623 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/w1/w1.h         |  112 ++++++++
 drivers/w1/w1_family.c  |  133 ++++++++++
 drivers/w1/w1_family.h  |   65 +++++
 drivers/w1/w1_int.c     |  207 +++++++++++++++
 drivers/w1/w1_int.h     |   36 ++
 drivers/w1/w1_io.c      |  138 ++++++++++
 drivers/w1/w1_io.h      |   35 ++
 drivers/w1/w1_log.h     |   38 ++
 drivers/w1/w1_netlink.c |   55 ++++
 drivers/w1/w1_netlink.h |   44 +++
 drivers/w1/w1_therm.c   |  177 +++++++++++++
 18 files changed, 1958 insertions(+)


diff -Nru a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS	2004-07-14 16:59:14 -07:00
+++ b/MAINTAINERS	2004-07-14 16:59:14 -07:00
@@ -2379,6 +2379,12 @@
 M:	kraxel@bytesex.org
 S:	Maintained
 
+W1 DALLAS'S 1-WIRE BUS
+P:	Evgeniy Polyakov
+M:	johnpol@2ka.mipt.ru
+L:	sensors@stimpy.netroedge.com
+S:	Maintained
+
 WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC)
 P:	Nenad Corbic
 M:	ncorbic@sangoma.com
diff -Nru a/drivers/Kconfig b/drivers/Kconfig
--- a/drivers/Kconfig	2004-07-14 16:59:14 -07:00
+++ b/drivers/Kconfig	2004-07-14 16:59:14 -07:00
@@ -42,6 +42,8 @@
 
 source "drivers/i2c/Kconfig"
 
+source "drivers/w1/Kconfig"
+
 source "drivers/misc/Kconfig"
 
 source "drivers/media/Kconfig"
diff -Nru a/drivers/Makefile b/drivers/Makefile
--- a/drivers/Makefile	2004-07-14 16:59:14 -07:00
+++ b/drivers/Makefile	2004-07-14 16:59:14 -07:00
@@ -42,6 +42,7 @@
 obj-$(CONFIG_SERIO)		+= input/serio/
 obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_I2C)		+= i2c/
+obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
 obj-$(CONFIG_BT)		+= bluetooth/
diff -Nru a/drivers/w1/Kconfig b/drivers/w1/Kconfig
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/Kconfig	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,31 @@
+menu "Dallas's 1-wire bus"
+
+config W1
+	tristate "Dallas's 1-wire support"
+	---help---
+	  Dallas's 1-wire bus is usefull to connect slow 1-pin devices 
+	  such as iButtons and thermal sensors.
+	  
+	  If you want W1 support, you should say Y here.
+
+	  This W1 support can also be built as a module.  If so, the module
+	  will be called wire.ko.
+
+config W1_MATROX
+	tristate "Matrox G400 transport layer for 1-wire"
+	depends on W1
+	help
+	  Say Y here if you want to communicate with your 1-wire devices
+	  using Matrox's G400 GPIO pins.
+	  
+	  This support is also available as a module.  If so, the module 
+	  will be called matrox_w1.ko.
+
+config W1_THERM
+	tristate "Thermal family implementation"
+	depends on W1
+	help
+	  Say Y here if you want to connect 1-wire thermal sensors to you
+	  wire.
+
+endmenu
diff -Nru a/drivers/w1/Makefile b/drivers/w1/Makefile
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/Makefile	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,9 @@
+#
+# Makefile for the Dallas's 1-wire bus.
+#
+
+obj-$(CONFIG_W1)	+= wire.o
+wire-objs		:= w1.o w1_int.o w1_family.o w1_netlink.o w1_io.o
+
+obj-$(CONFIG_W1_MATROX)		+= matrox_w1.o
+obj-$(CONFIG_W1_THERM)		+= w1_therm.o
diff -Nru a/drivers/w1/matrox_w1.c b/drivers/w1/matrox_w1.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/matrox_w1.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,246 @@
+/*
+ * 	matrox_w1.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+
+#include "w1.h"
+#include "w1_int.h"
+#include "w1_log.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire prtocol) over VGA DDC(matrox gpio).");
+
+static struct pci_device_id matrox_w1_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) },
+	{ },
+};
+MODULE_DEVICE_TABLE(pci, matrox_w1_tbl);
+
+static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *);
+static void __devexit matrox_w1_remove(struct pci_dev *);
+
+static struct pci_driver matrox_w1_pci_driver = {
+	.name = "matrox_w1",
+	.id_table = matrox_w1_tbl,
+	.probe = matrox_w1_probe,
+	.remove = __devexit_p(matrox_w1_remove),
+};
+
+/* 
+ * Matrox G400 DDC registers.
+ */
+
+#define MATROX_G400_DDC_CLK		(1<<4)
+#define MATROX_G400_DDC_DATA		(1<<1)
+
+#define MATROX_BASE			0x3C00
+#define MATROX_STATUS			0x1e14
+
+#define MATROX_PORT_INDEX_OFFSET	0x00
+#define MATROX_PORT_DATA_OFFSET		0x0A
+
+#define MATROX_GET_CONTROL		0x2A
+#define MATROX_GET_DATA			0x2B
+#define MATROX_CURSOR_CTL		0x06
+
+struct matrox_device
+{
+	unsigned long base_addr;
+	unsigned long port_index, port_data;
+	u8 data_mask;
+
+	unsigned long phys_addr, virt_addr;
+	unsigned long found;
+
+	struct w1_bus_master *bus_master;
+};
+
+static u8 matrox_w1_read_ddc_bit(unsigned long);
+static void matrox_w1_write_ddc_bit(unsigned long, u8);
+
+/*
+ * These functions read and write DDC Data bit.
+ *
+ * Using tristate pins, since i can't  fin any open-drain pin in whole motherboard.
+ * Unfortunately we can't connect to Intel's 82801xx IO controller
+ * since we don't know motherboard schema, wich has pretty unused(may be not) GPIO.
+ *
+ * I've heard that PIIX also has open drain pin.
+ *
+ * Port mapping.
+ */
+static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg)
+{
+	u8 ret;
+
+	writeb(reg, dev->port_index);
+	ret = readb(dev->port_data);
+	barrier();
+
+	return ret;
+}
+
+static __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val)
+{
+	writeb(reg, dev->port_index);
+	writeb(val, dev->port_data);
+	wmb();
+}
+
+static void matrox_w1_write_ddc_bit(unsigned long data, u8 bit)
+{
+	u8 ret;
+	struct matrox_device *dev = (struct matrox_device *) data;
+
+	if (bit)
+		bit = 0;
+	else
+		bit = dev->data_mask;
+
+	ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL);
+	matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit));
+	matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00);
+}
+
+static u8 matrox_w1_read_ddc_bit(unsigned long data)
+{
+	u8 ret;
+	struct matrox_device *dev = (struct matrox_device *) data;
+
+	ret = matrox_w1_read_reg(dev, MATROX_GET_DATA);
+
+	return ret;
+}
+
+static void matrox_w1_hw_init(struct matrox_device *dev)
+{
+	matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF);
+	matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);
+}
+
+static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct matrox_device *dev;
+	int err;
+
+	assert(pdev != NULL);
+	assert(ent != NULL);
+
+	if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400)
+		return -ENODEV;
+
+	dev = kmalloc(sizeof(struct matrox_device) +
+		       sizeof(struct w1_bus_master), GFP_KERNEL);
+	if (!dev) {
+		dev_err(&pdev->dev,
+			"%s: Failed to create new matrox_device object.\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	memset(dev, 0, sizeof(struct matrox_device) + sizeof(struct w1_bus_master));
+
+	dev->bus_master = (struct w1_bus_master *)(dev + 1);
+
+	/* 
+	 * True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c 
+	 */
+
+	dev->phys_addr = pci_resource_start(pdev, 1);
+
+	dev->virt_addr =
+		(unsigned long) ioremap_nocache(dev->phys_addr, 16384);
+	if (!dev->virt_addr) {
+		dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n",
+			__func__, dev->phys_addr, 16384);
+		err = -EIO;
+		goto err_out_free_device;
+	}
+
+	dev->base_addr = dev->virt_addr + MATROX_BASE;
+	dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET;
+	dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET;
+	dev->data_mask = (MATROX_G400_DDC_DATA);
+
+	matrox_w1_hw_init(dev);
+
+	dev->bus_master->data = (unsigned long) dev;
+	dev->bus_master->read_bit = &matrox_w1_read_ddc_bit;
+	dev->bus_master->write_bit = &matrox_w1_write_ddc_bit;
+
+	err = w1_add_master_device(dev->bus_master);
+	if (err)
+		goto err_out_free_device;
+
+	pci_set_drvdata(pdev, dev);
+
+	dev->found = 1;
+
+	dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n");
+
+	return 0;
+
+err_out_free_device:
+	kfree(dev);
+
+	return err;
+}
+
+static void __devexit matrox_w1_remove(struct pci_dev *pdev)
+{
+	struct matrox_device *dev = pci_get_drvdata(pdev);
+
+	assert(dev != NULL);
+
+	if (dev->found) {
+		w1_remove_master_device(dev->bus_master);
+		iounmap((void *) dev->virt_addr);
+	}
+	kfree(dev);
+}
+
+static int __init matrox_w1_init(void)
+{
+	return pci_module_init(&matrox_w1_pci_driver);
+}
+
+static void __exit matrox_w1_fini(void)
+{
+	pci_unregister_driver(&matrox_w1_pci_driver);
+}
+
+module_init(matrox_w1_init);
+module_exit(matrox_w1_fini);
diff -Nru a/drivers/w1/w1.c b/drivers/w1/w1.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,623 @@
+/*
+ * 	w1.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/atomic.h>
+#include <asm/delay.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+
+#include "w1.h"
+#include "w1_io.h"
+#include "w1_log.h"
+#include "w1_int.h"
+#include "w1_family.h"
+#include "w1_netlink.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol.");
+
+static int w1_timeout = 5 * HZ;
+int w1_max_slave_count = 10;
+
+module_param_named(timeout, w1_timeout, int, 0);
+module_param_named(max_slave_count, w1_max_slave_count, int, 0);
+
+spinlock_t w1_mlock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(w1_masters);
+
+static pid_t control_thread;
+static int control_needs_exit;
+static DECLARE_COMPLETION(w1_control_complete);
+static DECLARE_WAIT_QUEUE_HEAD(w1_control_wait);
+
+static int w1_master_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static int w1_master_probe(struct device *dev)
+{
+	return -ENODEV;
+}
+
+static int w1_master_remove(struct device *dev)
+{
+	return 0;
+}
+
+static void w1_master_release(struct device *dev)
+{
+	struct w1_master *md = container_of(dev, struct w1_master, dev);
+
+	complete(&md->dev_released);
+}
+
+static void w1_slave_release(struct device *dev)
+{
+	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+	complete(&sl->dev_released);
+}
+
+static ssize_t w1_default_read_name(struct device *dev, char *buf)
+{
+	return sprintf(buf, "No family registered.\n");
+}
+
+static ssize_t w1_default_read_bin(struct kobject *kobj, char *buf, loff_t off,
+		     size_t count)
+{
+	return sprintf(buf, "No family registered.\n");
+}
+
+struct bus_type w1_bus_type = {
+	.name = "w1",
+	.match = w1_master_match,
+};
+
+struct device_driver w1_driver = {
+	.name = "w1_driver",
+	.bus = &w1_bus_type,
+	.probe = w1_master_probe,
+	.remove = w1_master_remove,
+};
+
+struct device w1_device = {
+	.parent = NULL,
+	.bus = &w1_bus_type,
+	.bus_id = "w1 bus master",
+	.driver = &w1_driver,
+	.release = &w1_master_release
+};
+
+static struct device_attribute w1_slave_attribute = {
+	.attr = {
+			.name = "name",
+			.mode = S_IRUGO,
+			.owner = THIS_MODULE
+	},
+	.show = &w1_default_read_name,
+};
+
+static struct device_attribute w1_slave_attribute_val = {
+	.attr = {
+			.name = "value",
+			.mode = S_IRUGO,
+			.owner = THIS_MODULE
+	},
+	.show = &w1_default_read_name,
+};
+
+static ssize_t w1_master_attribute_show(struct device *dev, char *buf)
+{
+	return sprintf(buf, "please fix me\n");
+#if 0
+	struct w1_master *md = container_of(dev, struct w1_master, dev);
+	int c = PAGE_SIZE;
+
+	if (down_interruptible(&md->mutex))
+		return -EBUSY;
+
+	c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", md->name);
+	c -= snprintf(buf + PAGE_SIZE - c, c,
+		       "bus_master=0x%p, timeout=%d, max_slave_count=%d, attempts=%lu\n",
+		       md->bus_master, w1_timeout, md->max_slave_count,
+		       md->attempts);
+	c -= snprintf(buf + PAGE_SIZE - c, c, "%d slaves: ",
+		       md->slave_count);
+	if (md->slave_count == 0)
+		c -= snprintf(buf + PAGE_SIZE - c, c, "no.\n");
+	else {
+		struct list_head *ent, *n;
+		struct w1_slave *sl;
+
+		list_for_each_safe(ent, n, &md->slist) {
+			sl = list_entry(ent, struct w1_slave, w1_slave_entry);
+
+			c -= snprintf(buf + PAGE_SIZE - c, c, "%s[%p] ",
+				       sl->name, sl);
+		}
+		c -= snprintf(buf + PAGE_SIZE - c, c, "\n");
+	}
+
+	up(&md->mutex);
+
+	return PAGE_SIZE - c;
+#endif
+}
+
+struct device_attribute w1_master_attribute = {
+	.attr = {
+			.name = "w1_master_stats",
+			.mode = S_IRUGO,
+			.owner = THIS_MODULE,
+	},
+	.show = &w1_master_attribute_show,
+};
+
+static struct bin_attribute w1_slave_bin_attribute = {
+	.attr = {
+		 	.name = "w1_slave",
+		 	.mode = S_IRUGO,
+			.owner = THIS_MODULE,
+	},
+	.size = W1_SLAVE_DATA_SIZE,
+	.read = &w1_default_read_bin,
+};
+
+static int __w1_attach_slave_device(struct w1_slave *sl)
+{
+	int err;
+
+	sl->dev.parent = &sl->master->dev;
+	sl->dev.driver = sl->master->driver;
+	sl->dev.bus = &w1_bus_type;
+	sl->dev.release = &w1_slave_release;
+
+	snprintf(&sl->dev.bus_id[0], sizeof(sl->dev.bus_id),
+		  "%x-%llx",
+		  (unsigned int) sl->reg_num.family,
+		  (unsigned long long) sl->reg_num.id);
+	snprintf (&sl->name[0], sizeof(sl->name),
+		  "%x-%llx",
+		  (unsigned int) sl->reg_num.family,
+		  (unsigned long long) sl->reg_num.id);
+
+	dev_dbg(&sl->dev, "%s: registering %s.\n", __func__,
+		&sl->dev.bus_id[0]);
+
+	err = device_register(&sl->dev);
+	if (err < 0) {
+		dev_err(&sl->dev,
+			 "Device registration [%s] failed. err=%d\n",
+			 sl->dev.bus_id, err);
+		return err;
+	}
+
+	w1_slave_bin_attribute.read = sl->family->fops->rbin;
+	w1_slave_attribute.show = sl->family->fops->rname;
+	w1_slave_attribute_val.show = sl->family->fops->rval;
+	w1_slave_attribute_val.attr.name = sl->family->fops->rvalname;
+
+	err = device_create_file(&sl->dev, &w1_slave_attribute);
+	if (err < 0) {
+		dev_err(&sl->dev,
+			 "sysfs file creation for [%s] failed. err=%d\n",
+			 sl->dev.bus_id, err);
+		device_unregister(&sl->dev);
+		return err;
+	}
+
+	err = device_create_file(&sl->dev, &w1_slave_attribute_val);
+	if (err < 0) {
+		dev_err(&sl->dev,
+			 "sysfs file creation for [%s] failed. err=%d\n",
+			 sl->dev.bus_id, err);
+		device_remove_file(&sl->dev, &w1_slave_attribute);
+		device_unregister(&sl->dev);
+		return err;
+	}
+
+	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_slave_bin_attribute);
+	if (err < 0) {
+		dev_err(&sl->dev,
+			 "sysfs file creation for [%s] failed. err=%d\n",
+			 sl->dev.bus_id, err);
+		device_remove_file(&sl->dev, &w1_slave_attribute);
+		device_remove_file(&sl->dev, &w1_slave_attribute_val);
+		device_unregister(&sl->dev);
+		return err;
+	}
+
+	list_add_tail(&sl->w1_slave_entry, &sl->master->slist);
+
+	return 0;
+}
+
+static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
+{
+	struct w1_slave *sl;
+	struct w1_family *f;
+	int err;
+
+	sl = kmalloc(sizeof(struct w1_slave), GFP_KERNEL);
+	if (!sl) {
+		dev_err(&dev->dev,
+			 "%s: failed to allocate new slave device.\n",
+			 __func__);
+		return -ENOMEM;
+	}
+
+	memset(sl, 0, sizeof(*sl));
+
+	sl->owner = THIS_MODULE;
+	sl->master = dev;
+
+	memcpy(&sl->reg_num, rn, sizeof(sl->reg_num));
+	atomic_set(&sl->refcnt, 0);
+	init_completion(&sl->dev_released);
+
+	spin_lock(&w1_flock);
+	f = w1_family_registered(rn->family);
+	if (!f) {
+		spin_unlock(&w1_flock);
+		dev_info(&dev->dev, "Family %x is not registered.\n",
+			  rn->family);
+		kfree(sl);
+		return -ENODEV;
+	}
+	__w1_family_get(f);
+	spin_unlock(&w1_flock);
+
+	sl->family = f;
+
+
+	err = __w1_attach_slave_device(sl);
+	if (err < 0) {
+		dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__,
+			 sl->name);
+		w1_family_put(sl->family);
+		kfree(sl);
+		return err;
+	}
+
+	dev->slave_count++;
+
+	return 0;
+}
+
+static void w1_slave_detach(struct w1_slave *sl)
+{
+	dev_info(&sl->dev, "%s: detaching %s.\n", __func__, sl->name);
+
+	while (atomic_read(&sl->refcnt))
+		schedule_timeout(10);
+
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_bin_attribute);
+	device_remove_file(&sl->dev, &w1_slave_attribute);
+	device_unregister(&sl->dev);
+	w1_family_put(sl->family);
+}
+
+static void w1_search(struct w1_master *dev)
+{
+	u64 last, rn, tmp;
+	int i, count = 0, slave_count;
+	int last_family_desc, last_zero, last_device;
+	int search_bit, id_bit, comp_bit, desc_bit;
+	struct list_head *ent;
+	struct w1_slave *sl;
+	int family_found = 0;
+	struct w1_netlink_msg msg;
+
+	dev->attempts++;
+
+	memset(&msg, 0, sizeof(msg));
+
+	search_bit = id_bit = comp_bit = 0;
+	rn = tmp = last = 0;
+	last_device = last_zero = last_family_desc = 0;
+
+	desc_bit = 64;
+
+	while (!(id_bit && comp_bit) && !last_device
+		&& count++ < dev->max_slave_count) {
+		last = rn;
+		rn = 0;
+
+		last_family_desc = 0;
+
+		/*
+		 * Reset bus and all 1-wire device state machines
+		 * so they can respond to our requests.
+		 *
+		 * Return 0 - device(s) present, 1 - no devices present.
+		 */
+		if (w1_reset_bus(dev)) {
+			dev_info(&dev->dev, "No devices present on the wire.\n");
+			break;
+		}
+
+#if 1
+		memset(&msg, 0, sizeof(msg));
+
+		w1_write_8(dev, W1_SEARCH);
+		for (i = 0; i < 64; ++i) {
+			/*
+			 * Read 2 bits from bus.
+			 * All who don't sleep must send ID bit and COMPLEMENT ID bit.
+			 * They actually are ANDed between all senders.
+			 */
+			id_bit = w1_read_bit(dev);
+			comp_bit = w1_read_bit(dev);
+
+			if (id_bit && comp_bit)
+				break;
+
+			if (id_bit == 0 && comp_bit == 0) {
+				if (i == desc_bit)
+					search_bit = 1;
+				else if (i > desc_bit)
+					search_bit = 0;
+				else
+					search_bit = ((last >> i) & 0x1);
+
+				if (search_bit == 0) {
+					last_zero = i;
+					if (last_zero < 9)
+						last_family_desc = last_zero;
+				}
+
+			}
+			else
+				search_bit = id_bit;
+
+			tmp = search_bit;
+			rn |= (tmp << i);
+
+			/*
+			 * Write 1 bit to bus
+			 * and make all who don't have "search_bit" in "i"'th position
+			 * in it's registration number sleep.
+			 */
+			w1_write_bit(dev, search_bit);
+
+		}
+#endif
+		msg.id.w1_id = rn;
+		msg.val = w1_calc_crc8((u8 *) & rn, 7);
+		w1_netlink_send(dev, &msg);
+
+		if (desc_bit == last_zero)
+			last_device = 1;
+
+		desc_bit = last_zero;
+
+		slave_count = 0;
+		list_for_each(ent, &dev->slist) {
+			struct w1_reg_num *tmp;
+
+			tmp = (struct w1_reg_num *) &rn;
+
+			sl = list_entry(ent, struct w1_slave, w1_slave_entry);
+
+			if (sl->reg_num.family == tmp->family &&
+			    sl->reg_num.id == tmp->id &&
+			    sl->reg_num.crc == tmp->crc)
+				break;
+			else if (sl->reg_num.family == tmp->family) {
+				family_found = 1;
+				break;
+			}
+
+			slave_count++;
+		}
+
+		if (slave_count == dev->slave_count &&
+		    msg.val && (*((__u8 *) & msg.val) == msg.id.id.crc)) {
+			w1_attach_slave_device(dev, (struct w1_reg_num *) &rn);
+		}
+	}
+}
+
+int w1_control(void *data)
+{
+	struct w1_slave *sl;
+	struct w1_master *dev;
+	struct list_head *ent, *ment, *n, *mn;
+	int err, have_to_wait = 0, timeout;
+
+	daemonize("w1_control");
+	allow_signal(SIGTERM);
+
+	while (!control_needs_exit || have_to_wait) {
+		have_to_wait = 0;
+
+		timeout = w1_timeout;
+		do {
+			timeout = interruptible_sleep_on_timeout(&w1_control_wait, timeout);
+			if (current->flags & PF_FREEZE)
+				refrigerator(PF_FREEZE);
+		} while (!signal_pending(current) && (timeout > 0));
+
+		if (signal_pending(current))
+			flush_signals(current);
+
+		list_for_each_safe(ment, mn, &w1_masters) {
+			dev = list_entry(ment, struct w1_master, w1_master_entry);
+
+			if (!control_needs_exit && !dev->need_exit)
+				continue;
+			/*
+			 * Little race: we can create thread but not set the flag.
+			 * Get a chance for external process to set flag up.
+			 */
+			if (!dev->initialized) {
+				have_to_wait = 1;
+				continue;
+			}
+
+			spin_lock(&w1_mlock);
+			list_del(&dev->w1_master_entry);
+			spin_unlock(&w1_mlock);
+
+			if (control_needs_exit) {
+				dev->need_exit = 1;
+
+				err = kill_proc(dev->kpid, SIGTERM, 1);
+				if (err)
+					dev_err(&dev->dev,
+						 "Failed to send signal to w1 kernel thread %d.\n",
+						 dev->kpid);
+			}
+
+			wait_for_completion(&dev->dev_exited);
+
+			list_for_each_safe(ent, n, &dev->slist) {
+				sl = list_entry(ent, struct w1_slave, w1_slave_entry);
+
+				if (!sl)
+					dev_warn(&dev->dev,
+						  "%s: slave entry is NULL.\n",
+						  __func__);
+				else {
+					list_del(&sl->w1_slave_entry);
+
+					w1_slave_detach(sl);
+					kfree(sl);
+				}
+			}
+			device_remove_file(&dev->dev, &w1_master_attribute);
+			atomic_dec(&dev->refcnt);
+		}
+	}
+
+	complete_and_exit(&w1_control_complete, 0);
+}
+
+int w1_process(void *data)
+{
+	struct w1_master *dev = (struct w1_master *) data;
+	unsigned long timeout;
+
+	daemonize("%s", dev->name);
+	allow_signal(SIGTERM);
+
+	while (!dev->need_exit) {
+		timeout = w1_timeout;
+		do {
+			timeout = interruptible_sleep_on_timeout(&dev->kwait, timeout);
+			if (current->flags & PF_FREEZE)
+				refrigerator(PF_FREEZE);
+		} while (!signal_pending(current) && (timeout > 0));
+
+		if (signal_pending(current))
+			flush_signals(current);
+
+		if (dev->need_exit)
+			break;
+
+		if (!dev->initialized)
+			continue;
+
+		if (down_interruptible(&dev->mutex))
+			continue;
+		w1_search(dev);
+		up(&dev->mutex);
+	}
+
+	atomic_dec(&dev->refcnt);
+	complete_and_exit(&dev->dev_exited, 0);
+
+	return 0;
+}
+
+int w1_init(void)
+{
+	int retval;
+
+	printk(KERN_INFO "Driver for 1-wire Dallas network protocol.\n");
+
+	retval = bus_register(&w1_bus_type);
+	if (retval) {
+		printk(KERN_ERR "Failed to register bus. err=%d.\n", retval);
+		goto err_out_exit_init;
+	}
+
+	retval = driver_register(&w1_driver);
+	if (retval) {
+		printk(KERN_ERR
+			"Failed to register master driver. err=%d.\n",
+			retval);
+		goto err_out_bus_unregister;
+	}
+
+	control_thread = kernel_thread(&w1_control, NULL, 0);
+	if (control_thread < 0) {
+		printk(KERN_ERR "Failed to create control thread. err=%d\n",
+			control_thread);
+		retval = control_thread;
+		goto err_out_driver_unregister;
+	}
+
+	return 0;
+
+err_out_driver_unregister:
+	driver_unregister(&w1_driver);
+
+err_out_bus_unregister:
+	bus_unregister(&w1_bus_type);
+
+err_out_exit_init:
+	return retval;
+}
+
+void w1_fini(void)
+{
+	struct w1_master *dev;
+	struct list_head *ent, *n;
+
+	list_for_each_safe(ent, n, &w1_masters) {
+		dev = list_entry(ent, struct w1_master, w1_master_entry);
+		__w1_remove_master_device(dev);
+	}
+
+	control_needs_exit = 1;
+
+	wait_for_completion(&w1_control_complete);
+
+	driver_unregister(&w1_driver);
+	bus_unregister(&w1_bus_type);
+}
+
+module_init(w1_init);
+module_exit(w1_fini);
diff -Nru a/drivers/w1/w1.h b/drivers/w1/w1.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1.h	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,112 @@
+/*
+ * 	w1.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_H
+#define __W1_H
+
+struct w1_reg_num
+{
+	__u64	family:8,
+		id:48,
+		crc:8;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/completion.h>
+#include <linux/device.h>
+
+#include <net/sock.h>
+
+#include <asm/semaphore.h>
+
+#include "w1_family.h"
+
+#define W1_MAXNAMELEN		32
+#define W1_SLAVE_DATA_SIZE	128
+
+#define W1_SEARCH		0xF0
+#define W1_CONDITIONAL_SEARCH	0xEC
+#define W1_CONVERT_TEMP		0x44
+#define W1_SKIP_ROM		0xCC
+#define W1_READ_SCRATCHPAD	0xBE
+#define W1_READ_ROM		0x33
+#define W1_READ_PSUPPLY		0xB4
+#define W1_MATCH_ROM		0x55
+
+struct w1_slave
+{
+	struct module		*owner;
+	unsigned char		name[W1_MAXNAMELEN];
+	struct list_head	w1_slave_entry;
+	struct w1_reg_num	reg_num;
+	atomic_t		refcnt;
+	u8			rom[9];
+
+	struct w1_master	*master;
+	struct w1_family 	*family;
+	struct device 		dev;
+	struct completion 	dev_released;
+};
+
+struct w1_bus_master
+{
+	unsigned long		data;
+
+	u8			(*read_bit)(unsigned long);
+	void			(*write_bit)(unsigned long, u8);
+};
+
+struct w1_master
+{
+	struct list_head	w1_master_entry;
+	struct module		*owner;
+	unsigned char		name[W1_MAXNAMELEN];
+	struct list_head	slist;
+	int			max_slave_count, slave_count;
+	unsigned long		attempts;
+	int			initialized;
+	u32			id;
+
+	atomic_t		refcnt;
+
+	void			*priv;
+	int			priv_size;
+
+	int			need_exit;
+	pid_t			kpid;
+	wait_queue_head_t 	kwait;
+	struct semaphore 	mutex;
+
+	struct device_driver	*driver;
+	struct device 		dev;
+	struct completion 	dev_released;
+	struct completion 	dev_exited;
+
+	struct w1_bus_master	*bus_master;
+
+	u32			seq, groups;
+	struct sock 		*nls;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* __W1_H */
diff -Nru a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_family.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,133 @@
+/*
+ * 	w1_family.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#include "w1_family.h"
+
+spinlock_t w1_flock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(w1_families);
+
+int w1_register_family(struct w1_family *newf)
+{
+	struct list_head *ent, *n;
+	struct w1_family *f;
+	int ret = 0;
+
+	spin_lock(&w1_flock);
+	list_for_each_safe(ent, n, &w1_families) {
+		f = list_entry(ent, struct w1_family, family_entry);
+
+		if (f->fid == newf->fid) {
+			ret = -EEXIST;
+			break;
+		}
+	}
+
+	if (!ret) {
+		atomic_set(&newf->refcnt, 0);
+		newf->need_exit = 0;
+		list_add_tail(&newf->family_entry, &w1_families);
+	}
+
+	spin_unlock(&w1_flock);
+
+	return ret;
+}
+
+void w1_unregister_family(struct w1_family *fent)
+{
+	struct list_head *ent, *n;
+	struct w1_family *f;
+
+	spin_lock(&w1_flock);
+	list_for_each_safe(ent, n, &w1_families) {
+		f = list_entry(ent, struct w1_family, family_entry);
+
+		if (f->fid == fent->fid) {
+			list_del(&fent->family_entry);
+			break;
+		}
+	}
+
+	fent->need_exit = 1;
+
+	spin_unlock(&w1_flock);
+
+	while (atomic_read(&fent->refcnt))
+		schedule_timeout(10);
+}
+
+/*
+ * Should be called under w1_flock held.
+ */
+struct w1_family * w1_family_registered(u8 fid)
+{
+	struct list_head *ent, *n;
+	struct w1_family *f = NULL;
+	int ret = 0;
+
+	list_for_each_safe(ent, n, &w1_families) {
+		f = list_entry(ent, struct w1_family, family_entry);
+
+		if (f->fid == fid) {
+			ret = 1;
+			break;
+		}
+	}
+
+	return (ret) ? f : NULL;
+}
+
+void w1_family_put(struct w1_family *f)
+{
+	spin_lock(&w1_flock);
+	__w1_family_put(f);
+	spin_unlock(&w1_flock);
+}
+
+void __w1_family_put(struct w1_family *f)
+{
+	if (atomic_dec_and_test(&f->refcnt))
+		f->need_exit = 1;
+}
+
+void w1_family_get(struct w1_family *f)
+{
+	spin_lock(&w1_flock);
+	__w1_family_get(f);
+	spin_unlock(&w1_flock);
+
+}
+
+void __w1_family_get(struct w1_family *f)
+{
+	atomic_inc(&f->refcnt);
+}
+
+EXPORT_SYMBOL(w1_family_get);
+EXPORT_SYMBOL(w1_family_put);
+EXPORT_SYMBOL(__w1_family_get);
+EXPORT_SYMBOL(__w1_family_put);
+EXPORT_SYMBOL(w1_family_registered);
+EXPORT_SYMBOL(w1_unregister_family);
+EXPORT_SYMBOL(w1_register_family);
diff -Nru a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_family.h	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,65 @@
+/*
+ * 	w1_family.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_FAMILY_H
+#define __W1_FAMILY_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+#define W1_FAMILY_DEFAULT	0
+#define W1_FAMILY_THERM		0x10
+#define W1_FAMILY_IBUT		0xff /* ? */
+
+#define MAXNAMELEN		32
+
+struct w1_family_ops
+{
+	ssize_t (* rname)(struct device *, char *);
+	ssize_t (* rbin)(struct kobject *, char *, loff_t, size_t);
+	
+	ssize_t (* rval)(struct device *, char *);
+	unsigned char rvalname[MAXNAMELEN];
+};
+
+struct w1_family
+{
+	struct list_head	family_entry;
+	u8			fid;
+	
+	struct w1_family_ops	*fops;
+	
+	atomic_t		refcnt;
+	u8			need_exit;
+};
+
+extern spinlock_t w1_flock;
+
+void w1_family_get(struct w1_family *);
+void w1_family_put(struct w1_family *);
+void __w1_family_get(struct w1_family *);
+void __w1_family_put(struct w1_family *);
+struct w1_family * w1_family_registered(u8);
+void w1_unregister_family(struct w1_family *);
+int w1_register_family(struct w1_family *);
+
+#endif /* __W1_FAMILY_H */
diff -Nru a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_int.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,207 @@
+/*
+ * 	w1_int.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include "w1.h"
+#include "w1_log.h"
+
+static u32 w1_ids = 1;
+
+extern struct device_driver w1_driver;
+extern struct bus_type w1_bus_type;
+extern struct device w1_device;
+extern struct device_attribute w1_master_attribute;
+extern int w1_max_slave_count;
+extern struct list_head w1_masters;
+extern spinlock_t w1_mlock;
+
+extern int w1_process(void *);
+
+struct w1_master * w1_alloc_dev(u32 id, int slave_count,
+	      struct device_driver *driver, struct device *device)
+{
+	struct w1_master *dev;
+	int err;
+
+	/*
+	 * We are in process context(kernel thread), so can sleep.
+	 */
+	dev = kmalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL);
+	if (!dev) {
+		printk(KERN_ERR
+			"Failed to allocate %d bytes for new w1 device.\n",
+			sizeof(struct w1_master));
+		return NULL;
+	}
+
+	memset(dev, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
+
+	dev->bus_master = (struct w1_bus_master *)(dev + 1);
+
+	dev->owner 		= THIS_MODULE;
+	dev->max_slave_count 	= slave_count;
+	dev->slave_count 	= 0;
+	dev->attempts 		= 0;
+	dev->kpid 		= -1;
+	dev->initialized 	= 0;
+	dev->id 		= id;
+
+	atomic_set(&dev->refcnt, 2);
+
+	INIT_LIST_HEAD(&dev->slist);
+	init_MUTEX(&dev->mutex);
+
+	init_waitqueue_head(&dev->kwait);
+	init_completion(&dev->dev_released);
+	init_completion(&dev->dev_exited);
+
+	memcpy(&dev->dev, device, sizeof(struct device));
+	snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
+		  "w1_bus_master%u", dev->id);
+	snprintf(dev->name, sizeof(dev->name), "w1_bus_master%u", dev->id);
+
+	dev->driver = driver;
+
+	dev->groups = 23;
+	dev->seq = 1;
+	dev->nls = netlink_kernel_create(NETLINK_NFLOG, NULL);
+	if (!dev->nls) {
+		printk(KERN_ERR "Failed to create new netlink socket(%u).\n",
+			NETLINK_NFLOG);
+		memset(dev, 0, sizeof(struct w1_master));
+		kfree(dev);
+		dev = NULL;
+	}
+
+	err = device_register(&dev->dev);
+	if (err) {
+		printk(KERN_ERR "Failed to register master device. err=%d\n", err);
+		if (dev->nls->sk_socket)
+			sock_release(dev->nls->sk_socket);
+		memset(dev, 0, sizeof(struct w1_master));
+		kfree(dev);
+		dev = NULL;
+	}
+
+	return dev;
+}
+
+void w1_free_dev(struct w1_master *dev)
+{
+	device_unregister(&dev->dev);
+	if (dev->nls->sk_socket)
+		sock_release(dev->nls->sk_socket);
+	memset(dev, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
+	kfree(dev);
+}
+
+int w1_add_master_device(struct w1_bus_master *master)
+{
+	struct w1_master *dev;
+	int retval = 0;
+
+	dev = w1_alloc_dev(w1_ids++, w1_max_slave_count, &w1_driver, &w1_device);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->kpid = kernel_thread(&w1_process, dev, 0);
+	if (dev->kpid < 0) {
+		dev_err(&dev->dev,
+			 "Failed to create new kernel thread. err=%d\n",
+			 dev->kpid);
+		retval = dev->kpid;
+		goto err_out_free_dev;
+	}
+
+	retval = device_create_file(&dev->dev, &w1_master_attribute);
+	if (retval)
+		goto err_out_kill_thread;
+
+	memcpy(dev->bus_master, master, sizeof(struct w1_bus_master));
+
+	dev->initialized = 1;
+
+	spin_lock(&w1_mlock);
+	list_add(&dev->w1_master_entry, &w1_masters);
+	spin_unlock(&w1_mlock);
+
+	return 0;
+
+err_out_kill_thread:
+	dev->need_exit = 1;
+	if (kill_proc(dev->kpid, SIGTERM, 1))
+		dev_err(&dev->dev,
+			 "Failed to send signal to w1 kernel thread %d.\n",
+			 dev->kpid);
+	wait_for_completion(&dev->dev_exited);
+
+err_out_free_dev:
+	w1_free_dev(dev);
+
+	return retval;
+}
+
+void __w1_remove_master_device(struct w1_master *dev)
+{
+	int err;
+
+	dev->need_exit = 1;
+	err = kill_proc(dev->kpid, SIGTERM, 1);
+	if (err)
+		dev_err(&dev->dev,
+			 "%s: Failed to send signal to w1 kernel thread %d.\n",
+			 __func__, dev->kpid);
+
+	while (atomic_read(&dev->refcnt))
+		schedule_timeout(10);
+
+	w1_free_dev(dev);
+}
+
+void w1_remove_master_device(struct w1_bus_master *bm)
+{
+	struct w1_master *dev = NULL;
+	struct list_head *ent, *n;
+
+	list_for_each_safe(ent, n, &w1_masters) {
+		dev = list_entry(ent, struct w1_master, w1_master_entry);
+		if (!dev->initialized)
+			continue;
+
+		if (dev->bus_master->data == bm->data)
+			break;
+	}
+
+	if (!dev) {
+		printk(KERN_ERR "Device doesn't exist.\n");
+		return;
+	}
+
+	__w1_remove_master_device(dev);
+}
+
+EXPORT_SYMBOL(w1_alloc_dev);
+EXPORT_SYMBOL(w1_free_dev);
+EXPORT_SYMBOL(w1_add_master_device);
+EXPORT_SYMBOL(w1_remove_master_device);
+EXPORT_SYMBOL(__w1_remove_master_device);
diff -Nru a/drivers/w1/w1_int.h b/drivers/w1/w1_int.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_int.h	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,36 @@
+/*
+ * 	w1_int.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_INT_H
+#define __W1_INT_H
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include "w1.h"
+
+struct w1_master * w1_alloc_dev(int, struct device_driver *, struct device *);
+void w1_free_dev(struct w1_master *dev);
+int w1_add_master_device(struct w1_bus_master *);
+void w1_remove_master_device(struct w1_bus_master *);
+void __w1_remove_master_device(struct w1_master *);
+
+#endif /* __W1_INT_H */
diff -Nru a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_io.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,138 @@
+/*
+ * 	w1_io.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include <linux/moduleparam.h>
+
+#include "w1.h"
+#include "w1_log.h"
+#include "w1_io.h"
+
+int w1_delay_parm = 1;
+module_param_named(delay_coef, w1_delay_parm, int, 0);
+
+static u8 w1_crc8_table[] = {
+	0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
+	157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
+	35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
+	190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
+	70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
+	219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
+	101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
+	248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
+	140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
+	17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
+	175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
+	50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
+	202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
+	87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
+	233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
+	116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
+};
+
+void w1_delay(unsigned long tm)
+{
+	udelay(tm * w1_delay_parm);
+}
+
+void w1_write_bit(struct w1_master *dev, int bit)
+{
+	if (bit) {
+		dev->bus_master->write_bit(dev->bus_master->data, 0);
+		w1_delay(6);
+		dev->bus_master->write_bit(dev->bus_master->data, 1);
+		w1_delay(64);
+	} else {
+		dev->bus_master->write_bit(dev->bus_master->data, 0);
+		w1_delay(60);
+		dev->bus_master->write_bit(dev->bus_master->data, 1);
+		w1_delay(10);
+	}
+}
+
+void w1_write_8(struct w1_master *dev, u8 byte)
+{
+	int i;
+
+	for (i = 0; i < 8; ++i)
+		w1_write_bit(dev, (byte >> i) & 0x1);
+}
+
+u8 w1_read_bit(struct w1_master *dev)
+{
+	int result;
+
+	dev->bus_master->write_bit(dev->bus_master->data, 0);
+	w1_delay(6);
+	dev->bus_master->write_bit(dev->bus_master->data, 1);
+	w1_delay(9);
+
+	result = dev->bus_master->read_bit(dev->bus_master->data);
+	w1_delay(55);
+
+	return result & 0x1;
+}
+
+u8 w1_read_8(struct w1_master * dev)
+{
+	int i;
+	u8 res = 0;
+
+	for (i = 0; i < 8; ++i)
+		res |= (w1_read_bit(dev) << i);
+
+	return res;
+}
+
+int w1_reset_bus(struct w1_master *dev)
+{
+	int result;
+
+	dev->bus_master->write_bit(dev->bus_master->data, 0);
+	w1_delay(480);
+	dev->bus_master->write_bit(dev->bus_master->data, 1);
+	w1_delay(70);
+
+	result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1;
+	w1_delay(410);
+
+	return result;
+}
+
+u8 w1_calc_crc8(u8 * data, int len)
+{
+	u8 crc = 0;
+
+	while (len--)
+		crc = w1_crc8_table[crc ^ *data++];
+
+	return crc;
+}
+
+EXPORT_SYMBOL(w1_write_bit);
+EXPORT_SYMBOL(w1_write_8);
+EXPORT_SYMBOL(w1_read_bit);
+EXPORT_SYMBOL(w1_read_8);
+EXPORT_SYMBOL(w1_reset_bus);
+EXPORT_SYMBOL(w1_calc_crc8);
+EXPORT_SYMBOL(w1_delay);
diff -Nru a/drivers/w1/w1_io.h b/drivers/w1/w1_io.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_io.h	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,35 @@
+/*
+ * 	w1_io.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_IO_H
+#define __W1_IO_H
+
+#include "w1.h"
+
+void w1_delay(unsigned long);
+void w1_write_bit(struct w1_master *, int);
+void w1_write_8(struct w1_master *, u8);
+u8 w1_read_bit(struct w1_master *);
+u8 w1_read_8(struct w1_master *);
+int w1_reset_bus(struct w1_master *);
+u8 w1_calc_crc8(u8 *, int);
+
+#endif /* __W1_IO_H */
diff -Nru a/drivers/w1/w1_log.h b/drivers/w1/w1_log.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_log.h	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,38 @@
+/*
+ * 	w1_log.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_LOG_H
+#define __W1_LOG_H
+
+#define DEBUG
+
+#ifdef W1_DEBUG
+#  define assert(expr) do {} while (0)
+#else
+#  define assert(expr) \
+        if(unlikely(!(expr))) {				        \
+        printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n",	\
+        #expr,__FILE__,__FUNCTION__,__LINE__);		        \
+        }
+#endif
+
+#endif /* __W1_LOG_H */
+
diff -Nru a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_netlink.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,55 @@
+/*
+ * w1_netlink.c
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+
+#include "w1.h"
+#include "w1_log.h"
+#include "w1_netlink.h"
+
+void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
+{
+	unsigned int size;
+	struct sk_buff *skb;
+	struct w1_netlink_msg *data;
+	struct nlmsghdr *nlh;
+
+	size = NLMSG_SPACE(sizeof(struct w1_netlink_msg));
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb) {
+		dev_err(&dev->dev, "skb_alloc() failed.\n");
+		return;
+	}
+
+	nlh = NLMSG_PUT(skb, 0, dev->seq++, NLMSG_DONE, size - sizeof(*nlh));
+
+	data = (struct w1_netlink_msg *)NLMSG_DATA(nlh);
+
+	memcpy(data, msg, sizeof(struct w1_netlink_msg));
+
+	NETLINK_CB(skb).dst_groups = dev->groups;
+	netlink_broadcast(dev->nls, skb, 0, dev->groups, GFP_ATOMIC);
+
+nlmsg_failure:
+	return;
+}
diff -Nru a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_netlink.h	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,44 @@
+/*
+ * w1_netlink.h
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_NETLINK_H
+#define __W1_NETLINK_H
+
+#include <asm/types.h>
+
+#include "w1.h"
+
+struct w1_netlink_msg 
+{
+	union
+	{
+		struct w1_reg_num 	id;
+		__u64			w1_id;
+	} id;
+	__u64				val;
+};
+
+#ifdef __KERNEL__
+
+void w1_netlink_send(struct w1_master *, struct w1_netlink_msg *);
+
+#endif /* __KERNEL__ */
+#endif /* __W1_NETLINK_H */
diff -Nru a/drivers/w1/w1_therm.c b/drivers/w1/w1_therm.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/w1/w1_therm.c	2004-07-14 16:59:14 -07:00
@@ -0,0 +1,177 @@
+/*
+ * 	w1_therm.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the therms 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include "w1.h"
+#include "w1_io.h"
+#include "w1_int.h"
+#include "w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
+
+static ssize_t w1_therm_read_name(struct device *, char *);
+static ssize_t w1_therm_read_temp(struct device *, char *);
+static ssize_t w1_therm_read_bin(struct kobject *, char *, loff_t, size_t);
+
+static struct w1_family_ops w1_therm_fops = {
+	.rname = &w1_therm_read_name,
+	.rbin = &w1_therm_read_bin,
+	.rval = &w1_therm_read_temp,
+	.rvalname = "temp1_input",
+};
+
+static ssize_t w1_therm_read_name(struct device *dev, char *buf)
+{
+	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+	return sprintf(buf, "%s\n", sl->name);
+}
+
+static ssize_t w1_therm_read_temp(struct device *dev, char *buf)
+{
+	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+	s16 temp;
+
+	/* 
+	 * Must be more precise.
+	 */
+	temp = 0;
+	temp <<= sl->rom[1] / 2;
+	temp |= sl->rom[0] / 2;
+
+	return sprintf(buf, "%d\n", temp * 1000);
+}
+
+static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct w1_slave *sl = container_of(container_of(kobj, struct device, kobj),
+			      			struct w1_slave, dev);
+	struct w1_master *dev = sl->master;
+	u8 rom[9], crc, verdict;
+	size_t icount;
+	int i;
+	u16 temp;
+
+	atomic_inc(&sl->refcnt);
+	if (down_interruptible(&sl->master->mutex)) {
+		count = 0;
+		goto out_dec;
+	}
+
+	if (off > W1_SLAVE_DATA_SIZE) {
+		count = 0;
+		goto out;
+	}
+	if (off + count > W1_SLAVE_DATA_SIZE)
+		count = W1_SLAVE_DATA_SIZE - off;
+
+	icount = count;
+
+	memset(buf, 0, count);
+	memset(rom, 0, sizeof(rom));
+
+	count = 0;
+	verdict = 0;
+	crc = 0;
+	if (!w1_reset_bus(dev)) {
+		u64 id = *(u64 *) & sl->reg_num;
+		int count = 0;
+
+		w1_write_8(dev, W1_MATCH_ROM);
+		for (i = 0; i < 8; ++i)
+			w1_write_8(dev, (id >> i * 8) & 0xff);
+
+		w1_write_8(dev, W1_CONVERT_TEMP);
+
+		while (dev->bus_master->read_bit(dev->bus_master->data) == 0
+		       && count < 10) {
+			w1_delay(1);
+			count++;
+		}
+
+		if (count < 10) {
+			if (!w1_reset_bus(dev)) {
+				w1_write_8(dev, W1_MATCH_ROM);
+				for (i = 0; i < 8; ++i)
+					w1_write_8(dev,
+						   (id >> i * 8) & 0xff);
+
+				w1_write_8(dev, W1_READ_SCRATCHPAD);
+				for (i = 0; i < 9; ++i)
+					rom[i] = w1_read_8(dev);
+
+				crc = w1_calc_crc8(rom, 8);
+
+				if (rom[8] == crc && rom[0])
+					verdict = 1;
+			}
+		}
+		else
+			dev_warn(&dev->dev,
+				  "18S20 doesn't respond to CONVERT_TEMP.\n");
+	}
+
+	for (i = 0; i < 9; ++i)
+		count += snprintf(buf + count, icount - count, "%02x ", rom[i]);
+	count += snprintf(buf + count, icount - count, ": crc=%02x %s\n",
+			   crc, (verdict) ? "YES" : "NO");
+	if (verdict)
+		memcpy(sl->rom, rom, sizeof(sl->rom));
+	for (i = 0; i < 9; ++i)
+		count += snprintf(buf + count, icount - count, "%02x ", sl->rom[i]);
+	temp = 0;
+	temp <<= sl->rom[1] / 2;
+	temp |= sl->rom[0] / 2;
+	count += snprintf(buf + count, icount - count, "t=%u\n", temp);
+out:
+	up(&dev->mutex);
+out_dec:
+	atomic_dec(&sl->refcnt);
+
+	return count;
+}
+
+static struct w1_family w1_therm_family = {
+	.fid = W1_FAMILY_THERM,
+	.fops = &w1_therm_fops,
+};
+
+static int __init w1_therm_init(void)
+{
+	return w1_register_family(&w1_therm_family);
+}
+
+static void __exit w1_therm_fini(void)
+{
+	w1_unregister_family(&w1_therm_family);
+}
+
+module_init(w1_therm_init);
+module_exit(w1_therm_fini);


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                         ` Greg KH
@ 2004-07-15  0:07                           ` Greg KH
  2004-07-15  0:07                             ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.14, 2004/07/14 11:51:29-07:00, khali@linux-fr.org

[PATCH] I2C: Refine detection of LM75 chips

The LM75 detection method was a bit loose so far and would accept
non-LM75-compatible chips from times to times. It should be better now.
Additionally, the help for the lm75 driver was reworked because we now
know that the LM75 and the LM77 are not compatible.


Signed-off-by: Jean Delvare <khali at linux-fr dot org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/Kconfig |    8 ++++++--
 drivers/i2c/chips/lm75.c  |   37 +++++++++++++++++++++++++++++++------
 2 files changed, 37 insertions(+), 8 deletions(-)


diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig	2004-07-14 16:59:00 -07:00
+++ b/drivers/i2c/chips/Kconfig	2004-07-14 16:59:00 -07:00
@@ -103,8 +103,12 @@
 	select I2C_SENSOR
 	help
 	  If you say yes here you get support for National Semiconductor LM75
-	  sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon
-	  TCN75, and National Semiconductor LM77.
+	  sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in
+	  9-bit precision mode), and TelCom (now Microchip) TCN75.
+
+	  The DS75 and DS1775 in 10- to 12-bit precision modes will require
+	  a force module parameter. The driver will not handle the extra
+	  precision anyhow.
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm75.
diff -Nru a/drivers/i2c/chips/lm75.c b/drivers/i2c/chips/lm75.c
--- a/drivers/i2c/chips/lm75.c	2004-07-14 16:59:00 -07:00
+++ b/drivers/i2c/chips/lm75.c	2004-07-14 16:59:00 -07:00
@@ -113,7 +113,7 @@
 /* This function is called by i2c_detect */
 static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
 {
-	int i, cur, conf, hyst, os;
+	int i;
 	struct i2c_client *new_client;
 	struct lm75_data *data;
 	int err = 0;
@@ -149,16 +149,41 @@
 	new_client->driver = &lm75_driver;
 	new_client->flags = 0;
 
-	/* Now, we do the remaining detection. It is lousy. */
+	/* Now, we do the remaining detection. There is no identification-
+	   dedicated register so we have to rely on several tricks:
+	   unused bits, registers cycling over 8-address boundaries,
+	   addresses 0x04-0x07 returning the last read value.
+	   The cycling+unused addresses combination is not tested,
+	   since it would significantly slow the detection down and would
+	   hardly add any value. */
 	if (kind < 0) {
+		int cur, conf, hyst, os;
+
+		/* Unused addresses */
 		cur = i2c_smbus_read_word_data(new_client, 0);
 		conf = i2c_smbus_read_byte_data(new_client, 1);
 		hyst = i2c_smbus_read_word_data(new_client, 2);
+		if (i2c_smbus_read_word_data(new_client, 4) != hyst
+		 || i2c_smbus_read_word_data(new_client, 5) != hyst
+		 || i2c_smbus_read_word_data(new_client, 6) != hyst
+		 || i2c_smbus_read_word_data(new_client, 7) != hyst)
+		 	goto exit_free;
 		os = i2c_smbus_read_word_data(new_client, 3);
-		for (i = 0; i <= 0x1f; i++)
-			if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) ||
-			    (i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) ||
-			    (i2c_smbus_read_word_data(new_client, i * 8 + 3) != os))
+		if (i2c_smbus_read_word_data(new_client, 4) != os
+		 || i2c_smbus_read_word_data(new_client, 5) != os
+		 || i2c_smbus_read_word_data(new_client, 6) != os
+		 || i2c_smbus_read_word_data(new_client, 7) != os)
+		 	goto exit_free;
+
+		/* Unused bits */
+		if (conf & 0xe0)
+		 	goto exit_free;
+
+		/* Addresses cycling */
+		for (i = 8; i < 0xff; i += 8)
+			if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+			 || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+			 || i2c_smbus_read_word_data(new_client, i + 3) != os)
 				goto exit_free;
 	}
 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                       ` Greg KH
@ 2004-07-15  0:07                         ` Greg KH
  2004-07-15  0:07                           ` Greg KH
  2004-07-16 17:07                         ` Pavel Machek
  1 sibling, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.13.13, 2004/07/14 11:50:09-07:00, mhoffman@lightlink.com

[PATCH] I2C: Remove extra inits from lm78 driver

This patch is from the lm_sensors project CVS, from this revision:

	1.63 (mds) remove initialization of limits by driver

It is better to set these limits by a combination of /etc/sensors.conf
and 'sensors -s'; "mechanism not policy."  Please apply.

Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/i2c/chips/lm78.c |   88 -----------------------------------------------
 1 files changed, 88 deletions(-)


diff -Nru a/drivers/i2c/chips/lm78.c b/drivers/i2c/chips/lm78.c
--- a/drivers/i2c/chips/lm78.c	2004-07-14 16:59:07 -07:00
+++ b/drivers/i2c/chips/lm78.c	2004-07-14 16:59:07 -07:00
@@ -123,55 +123,6 @@
 }
 #define DIV_FROM_REG(val) (1 << (val))
 
-/* Initial limits. To keep them sane, we use the 'standard' translation as
-   specified in the LM78 sheet. Use the config file to set better limits. */
-#define LM78_INIT_IN_0(vid) ((vid)==3500 ? 2800 : (vid))
-#define LM78_INIT_IN_1(vid) ((vid)==3500 ? 2800 : (vid))
-#define LM78_INIT_IN_2 3300
-#define LM78_INIT_IN_3 (((5000)   * 100)/168)
-#define LM78_INIT_IN_4 (((12000)  * 10)/38)
-#define LM78_INIT_IN_5 (((-12000) * -604)/2100)
-#define LM78_INIT_IN_6 (((-5000)  * -604)/909)
-
-#define LM78_INIT_IN_PERCENTAGE 10
-
-#define LM78_INIT_IN_MIN_0(vid) (LM78_INIT_IN_0(vid) - \
-	LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_0(vid) (LM78_INIT_IN_0(vid) + \
-	LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MIN_1(vid) (LM78_INIT_IN_1(vid) - \
-	LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_1(vid) (LM78_INIT_IN_1(vid) + \
-	LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100)
-
-#define LM78_INIT_IN_MIN_2 \
-        (LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_2 \
-        (LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MIN_3 \
-        (LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_3 \
-        (LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MIN_4 \
-        (LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_4 \
-        (LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MIN_5 \
-        (LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_5 \
-        (LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MIN_6 \
-        (LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
-#define LM78_INIT_IN_MAX_6 \
-        (LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
-
-#define LM78_INIT_FAN_MIN_1 3000
-#define LM78_INIT_FAN_MIN_2 3000
-#define LM78_INIT_FAN_MIN_3 3000
-
-#define LM78_INIT_TEMP_OVER 60000
-#define LM78_INIT_TEMP_HYST 50000
-
 /* There are some complications in a module like this. First off, LM78 chips
    may be both present on the SMBus and the ISA bus, and we have to handle
    those cases separately at some places. Second, there might be several
@@ -755,45 +706,6 @@
 	else
 		vid |= 0x10;
 	vid = VID_FROM_REG(vid);
-
-	lm78_write_value(client, LM78_REG_IN_MIN(0),
-			 IN_TO_REG(LM78_INIT_IN_MIN_0(vid)));
-	lm78_write_value(client, LM78_REG_IN_MAX(0),
-			 IN_TO_REG(LM78_INIT_IN_MAX_0(vid)));
-	lm78_write_value(client, LM78_REG_IN_MIN(1),
-			 IN_TO_REG(LM78_INIT_IN_MIN_1(vid)));
-	lm78_write_value(client, LM78_REG_IN_MAX(1),
-			 IN_TO_REG(LM78_INIT_IN_MAX_1(vid)));
-	lm78_write_value(client, LM78_REG_IN_MIN(2),
-			 IN_TO_REG(LM78_INIT_IN_MIN_2));
-	lm78_write_value(client, LM78_REG_IN_MAX(2),
-			 IN_TO_REG(LM78_INIT_IN_MAX_2));
-	lm78_write_value(client, LM78_REG_IN_MIN(3),
-			 IN_TO_REG(LM78_INIT_IN_MIN_3));
-	lm78_write_value(client, LM78_REG_IN_MAX(3),
-			 IN_TO_REG(LM78_INIT_IN_MAX_3));
-	lm78_write_value(client, LM78_REG_IN_MIN(4),
-			 IN_TO_REG(LM78_INIT_IN_MIN_4));
-	lm78_write_value(client, LM78_REG_IN_MAX(4),
-			 IN_TO_REG(LM78_INIT_IN_MAX_4));
-	lm78_write_value(client, LM78_REG_IN_MIN(5),
-			 IN_TO_REG(LM78_INIT_IN_MIN_5));
-	lm78_write_value(client, LM78_REG_IN_MAX(5),
-			 IN_TO_REG(LM78_INIT_IN_MAX_5));
-	lm78_write_value(client, LM78_REG_IN_MIN(6),
-			 IN_TO_REG(LM78_INIT_IN_MIN_6));
-	lm78_write_value(client, LM78_REG_IN_MAX(6),
-			 IN_TO_REG(LM78_INIT_IN_MAX_6));
-	lm78_write_value(client, LM78_REG_FAN_MIN(0),
-			 FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2));
-	lm78_write_value(client, LM78_REG_FAN_MIN(1),
-			 FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2));
-	lm78_write_value(client, LM78_REG_FAN_MIN(2),
-			 FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2));
-	lm78_write_value(client, LM78_REG_TEMP_OVER,
-			 TEMP_TO_REG(LM78_INIT_TEMP_OVER));
-	lm78_write_value(client, LM78_REG_TEMP_HYST,
-			 TEMP_TO_REG(LM78_INIT_TEMP_HYST));
 
 	/* Start monitoring */
 	lm78_write_value(client, LM78_REG_CONFIG,


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                           ` Greg KH
@ 2004-07-15  0:07                             ` Greg KH
  0 siblings, 0 replies; 32+ messages in thread
From: Greg KH @ 2004-07-15  0:07 UTC (permalink / raw)
  To: linux-kernel, sensors

ChangeSet 1.1784.1.96, 2004/07/14 16:04:27-07:00, orange@fobie.net

[PATCH] I2C: patch quirks.c - SMBus hidden on hp laptop

This patch unhides the SMBus on the hp nc8000 and nc6000 laptops. The
patch has been co-written by Jean Delvare and Rudolf Marek. I've only
tested this on nc8000, but it should work for the nc6000 too.

Unfortunatley, we had to little information to fix the problem described
in the reported bug below, as is probably the same problem. But if we're
very lucky it might solve it too.
http://bugzilla.kernel.org/show_bug.cgi?id=2976


Signed-off-by: Örjan Persson <orange@fobie.net>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>


 drivers/pci/quirks.c |   58 ++++++++++++++++++++++++++++++---------------------
 1 files changed, 35 insertions(+), 23 deletions(-)


diff -Nru a/drivers/pci/quirks.c b/drivers/pci/quirks.c
--- a/drivers/pci/quirks.c	2004-07-14 16:58:40 -07:00
+++ b/drivers/pci/quirks.c	2004-07-14 16:58:40 -07:00
@@ -694,29 +694,38 @@
 
 static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
 {
-	if (likely(dev->subsystem_vendor != PCI_VENDOR_ID_ASUSTEK))
-		return;
-
-	if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB)
-		switch(dev->subsystem_device) {
-		case 0x8070: /* P4B */
-	    	case 0x8088: /* P4B533 */
-			asus_hides_smbus = 1;
-		}
-	if (dev->device == PCI_DEVICE_ID_INTEL_82845G_HB)
-		switch(dev->subsystem_device) {
- 		case 0x80b1: /* P4GE-V */
-		case 0x80b2: /* P4PE */
- 		case 0x8093: /* P4B533-V */
-			asus_hides_smbus = 1;
-		}
-	if ((dev->device == PCI_DEVICE_ID_INTEL_82850_HB) &&
-	    (dev->subsystem_device == 0x8030)) /* P4T533 */
-		asus_hides_smbus = 1;
-	if ((dev->device == PCI_DEVICE_ID_INTEL_7205_0) &&
-	    (dev->subsystem_device == 0x8070)) /* P4G8X Deluxe */
-		asus_hides_smbus = 1;
-	return;
+	if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK)) {
+		if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB)
+			switch(dev->subsystem_device) {
+			case 0x8070: /* P4B */
+			case 0x8088: /* P4B533 */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82845G_HB)
+			switch(dev->subsystem_device) {
+			case 0x80b1: /* P4GE-V */
+			case 0x80b2: /* P4PE */
+			case 0x8093: /* P4B533-V */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82850_HB)
+			switch(dev->subsystem_device) {
+			case 0x8030: /* P4T533 */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_7205_0)
+			switch (dev->subsystem_device) {
+			case 0x8070: /* P4G8X Deluxe */
+				asus_hides_smbus = 1;
+			}
+	} else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_HP)) {
+		if (dev->device ==  PCI_DEVICE_ID_INTEL_82855PM_HB)
+			switch(dev->subsystem_device) {
+			case 0x088C: /* HP Compaq nc8000 */
+			case 0x0890: /* HP Compaq nc6000 */
+				asus_hides_smbus = 1;
+			}
+	}
 }
 
 static void __init asus_hides_smbus_lpc(struct pci_dev *dev)
@@ -987,13 +996,16 @@
 
 	/*
 	 * on Asus P4B boards, the i801SMBus device is disabled at startup.
+	 * this also goes for boards in HP Compaq nc6000 and nc8000 notebooks.
 	 */
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82845_HB,	asus_hides_smbus_hostbridge },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82845G_HB,	asus_hides_smbus_hostbridge },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82850_HB,	asus_hides_smbus_hostbridge },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_7205_0,	asus_hides_smbus_hostbridge },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82855PM_HB,	asus_hides_smbus_hostbridge },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801DB_0,	asus_hides_smbus_lpc },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801BA_0,	asus_hides_smbus_lpc },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801DB_12,	asus_hides_smbus_lpc },
 
 #ifdef CONFIG_SCSI_SATA
 	/* Fixup BIOSes that configure Parallel ATA (PATA / IDE) and


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:07                       ` Greg KH
  2004-07-15  0:07                         ` Greg KH
@ 2004-07-16 17:07                         ` Pavel Machek
  2004-07-16 17:17                           ` Greg KH
  1 sibling, 1 reply; 32+ messages in thread
From: Pavel Machek @ 2004-07-16 17:07 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, sensors

Hi!

> +menu "Dallas's 1-wire bus"
> +
> +config W1
> +	tristate "Dallas's 1-wire support"
> +	---help---
> +	  Dallas's 1-wire bus is usefull to connect slow 1-pin devices 
> +	  such as iButtons and thermal sensors.

Just out of curiosity... are such devices really connected using one wire only,
or is it GND+5V+one data wire, or GND+power&data wire?

-- 
64 bytes from 195.113.31.123: icmp_seq=28 ttl=51 time=448769.1 ms         


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-16 17:07                         ` Pavel Machek
@ 2004-07-16 17:17                           ` Greg KH
  2004-07-16 17:39                             ` Bob Riegelmann
  2004-07-16 18:19                             ` Adam Kropelin
  0 siblings, 2 replies; 32+ messages in thread
From: Greg KH @ 2004-07-16 17:17 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, sensors

On Fri, Jul 16, 2004 at 07:07:16PM +0200, Pavel Machek wrote:
> Hi!
> 
> > +menu "Dallas's 1-wire bus"
> > +
> > +config W1
> > +	tristate "Dallas's 1-wire support"
> > +	---help---
> > +	  Dallas's 1-wire bus is usefull to connect slow 1-pin devices 
> > +	  such as iButtons and thermal sensors.
> 
> Just out of curiosity... are such devices really connected using one wire only,
> or is it GND+5V+one data wire, or GND+power&data wire?

I'm pretty sure it's just 1 wire, at least for the devices I've seen.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-16 17:17                           ` Greg KH
@ 2004-07-16 17:39                             ` Bob Riegelmann
  2004-07-16 18:19                             ` Adam Kropelin
  1 sibling, 0 replies; 32+ messages in thread
From: Bob Riegelmann @ 2004-07-16 17:39 UTC (permalink / raw)
  To: Greg KH, Pavel Machek; +Cc: linux-kernel, sensors

On Fri, 16 Jul 2004 10:17:03 -0700, Greg KH <greg@kroah.com> wrote:

> On Fri, Jul 16, 2004 at 07:07:16PM +0200, Pavel Machek wrote:
<snip>
>> Just out of curiosity... are such devices really connected using one 
>> wire only,
>> or is it GND+5V+one data wire, or GND+power&data wire?
>
> I'm pretty sure it's just 1 wire, at least for the devices I've seen.

I work with these all the time - it's power & data one one pin, and some 
kind of a ground.  They also make some in 3 terminal packages (pinouts can 
be had on the Maxim site). That said, I'm really pleased to see this in 
the kernel - as I'm in the process of developing all the firmware to port 
one of our product to use Linux! The application guys are way ahead of me, 
however...

Enjoy

Bob Riegelmann


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH] I2C update for 2.6.8-rc1
  2004-07-16 17:17                           ` Greg KH
  2004-07-16 17:39                             ` Bob Riegelmann
@ 2004-07-16 18:19                             ` Adam Kropelin
  1 sibling, 0 replies; 32+ messages in thread
From: Adam Kropelin @ 2004-07-16 18:19 UTC (permalink / raw)
  To: Greg KH; +Cc: Pavel Machek, linux-kernel, sensors

On Fri, Jul 16, 2004 at 10:17:03AM -0700, Greg KH wrote:
> On Fri, Jul 16, 2004 at 07:07:16PM +0200, Pavel Machek wrote:
> > Hi!
> > 
> > > +menu "Dallas's 1-wire bus"
> > > +
> > > +config W1
> > > +	tristate "Dallas's 1-wire support"
> > > +	---help---
> > > +	  Dallas's 1-wire bus is usefull to connect slow 1-pin devices 
> > > +	  such as iButtons and thermal sensors.
> > 
> > Just out of curiosity... are such devices really connected using one wire only,
> > or is it GND+5V+one data wire, or GND+power&data wire?
> 
> I'm pretty sure it's just 1 wire, at least for the devices I've seen.

It's GND+power&data. The device derives its power from transitions on
the data line driven by the master. Consequently there are some fairly
strict timing requirements on those pulses. Some devices can also be
powered by a dedicated VCC line which generally allows the device to
respond faster. A temperature sensor IC, for example, can usually
complete a conversion cycle much faster when it has dedicated power.

--Adam


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Greg (or anyone else) one small i2c question.
  2004-07-15  0:05 [BK PATCH] I2C update for 2.6.8-rc1 Greg KH
  2004-07-15  0:07 ` [PATCH] " Greg KH
@ 2004-07-17 14:30 ` Reinder
  2004-07-30  5:40   ` --- " Reinder
  2004-08-25  6:44   ` Greg " Greg KH
  2004-08-24 21:58 ` [BK PATCH] I2C update for 2.6.8-rc1 Alex Williamson
  2 siblings, 2 replies; 32+ messages in thread
From: Reinder @ 2004-07-17 14:30 UTC (permalink / raw)
  To: linux-kernel

Greg,

While having some fun, with the old 2.4 vt8231, ""porting"" that to the 2.6
Branche, my eyes raised some strange question, which I hope you can answer.

>From the Sysf-Interface, it states:

temp[1-4]_max	Temperature max value.
		Unit: Milidegrees Celsius
		Read/Write value.


I far as I know, one Milidegrees is one thousand Grade Celsius.

The actual Reading, from the vt8231 inside the io space is an 8 bit value.
It contains for example a number "78" in "Celsius" for de 1f CPU temp
location.

Now, from various documentations floating around, for some undocumented or
well hidden reason, this mostly is multiplied by 0.88 giving a reading of
68.84

I assume somebody thought about it and calculated that m that was a nice
correction value.

Converting it to the required units, would be 68.84*1000 = 68840
Or, to avoid Floating points: ( (68*1000)*880)/1000 = 68840 Milidegrees
Celsius


That was, when life was easy.

However, my bet is somebody figured out, that bit 7 till 6 in the reserved
area from area 4b, contains also bits of temperature information. (Although
the datasheet I have, describe that area as "Reserved") 

The value is readed, Anded with 192, then shifted to right by 6 to get the
reading.  "0,1,2,3". We assume the value is 3.

Assuming that, this Value is stored in the same Magic Celsius Value,
Multiplied by 880, this gives a correction of 2640 Milidegrees.

That is, if it where separate 8 and 2 bit values. But As I can see, from the
original code, it assumed as 1 10bit value.

Thus the first value 79 is shifted to left by 2, and the second value is
orred into it.  Which gives (79>>2) = 316. 
Plus the low value "3" = 319  

Returning, this, in the old version, to Human readable format, it was
multiplied by 10, and then divided by 4. Giving 797.5

I assume, that something has changed, with the sensor output format.
Thus, the new return formula would be: 
	Multi by 1000, divide by 4.
 Now we have 79750. 



Throwing in the 0.088 Correction Factor the final result is: 
79750* 0.088 = 70180 Milidegrees Celsius

Or 319 * 880 /4 = 70180

Or 319 * 220 = 70180

If this was "temp3" , it would result in 1 sensors.conf line:

Compute temp3 @



Is my final concussing Right? 

If so, I will move on to the voltage part :)
If not, I Hope somebody can point me out where to go...


http://tser.org/vt8231/

		Regards
			Reinder Kraaij.






^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: --- (or anyone else) one small i2c question.
  2004-07-17 14:30 ` Greg (or anyone else) one small i2c question Reinder
@ 2004-07-30  5:40   ` Reinder
  2004-07-30  6:30     ` Denis Vlasenko
  2004-08-25  6:44   ` Greg " Greg KH
  1 sibling, 1 reply; 32+ messages in thread
From: Reinder @ 2004-07-30  5:40 UTC (permalink / raw)
  To: linux-kernel

Hm, Greg must be on virtual holiday.

Yoo-hoo?

Anyone else, then alive?

I hope open source doesn't mean "Closed community :)"
Here I am. I want to contribute some code. But I need some help, because not
everything can be done alone on this planet. Sometimes you need guidance.

It's better to talk, and send people away on a search, then to ignore
people.

And all I want is some small help with i2c.

Reinder.







-----Original Message-----
From: linux-kernel-owner@vger.kernel.org
[mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Reinder
Sent: Saturday, July 17, 2004 16:30
To: linux-kernel@vger.kernel.org
Subject: Greg (or anyone else) one small i2c question. 

Greg,

While having some fun, with the old 2.4 vt8231, ""porting"" that to the 2.6
Branche, my eyes raised some strange question, which I hope you can answer.

>From the Sysf-Interface, it states:

temp[1-4]_max	Temperature max value.
		Unit: Milidegrees Celsius
		Read/Write value.


I far as I know, one Milidegrees is one thousand Grade Celsius.

The actual Reading, from the vt8231 inside the io space is an 8 bit value.
It contains for example a number "78" in "Celsius" for de 1f CPU temp
location.

Now, from various documentations floating around, for some undocumented or
well hidden reason, this mostly is multiplied by 0.88 giving a reading of
68.84

I assume somebody thought about it and calculated that m that was a nice
correction value.

Converting it to the required units, would be 68.84*1000 = 68840
Or, to avoid Floating points: ( (68*1000)*880)/1000 = 68840 Milidegrees
Celsius


That was, when life was easy.

However, my bet is somebody figured out, that bit 7 till 6 in the reserved
area from area 4b, contains also bits of temperature information. (Although
the datasheet I have, describe that area as "Reserved") 

The value is readed, Anded with 192, then shifted to right by 6 to get the
reading.  "0,1,2,3". We assume the value is 3.

Assuming that, this Value is stored in the same Magic Celsius Value,
Multiplied by 880, this gives a correction of 2640 Milidegrees.

That is, if it where separate 8 and 2 bit values. But As I can see, from the
original code, it assumed as 1 10bit value.

Thus the first value 79 is shifted to left by 2, and the second value is
orred into it.  Which gives (79>>2) = 316. 
Plus the low value "3" = 319  

Returning, this, in the old version, to Human readable format, it was
multiplied by 10, and then divided by 4. Giving 797.5

I assume, that something has changed, with the sensor output format.
Thus, the new return formula would be: 
	Multi by 1000, divide by 4.
 Now we have 79750. 



Throwing in the 0.088 Correction Factor the final result is: 
79750* 0.088 = 70180 Milidegrees Celsius

Or 319 * 880 /4 = 70180

Or 319 * 220 = 70180

If this was "temp3" , it would result in 1 sensors.conf line:

Compute temp3 @



Is my final concussing Right? 

If so, I will move on to the voltage part :)
If not, I Hope somebody can point me out where to go...


http://tser.org/vt8231/

		Regards
			Reinder Kraaij.





-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: --- (or anyone else) one small i2c question.
  2004-07-30  5:40   ` --- " Reinder
@ 2004-07-30  6:30     ` Denis Vlasenko
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Vlasenko @ 2004-07-30  6:30 UTC (permalink / raw)
  To: Reinder, linux-kernel

On Friday 30 July 2004 08:40, Reinder wrote:
> Hm, Greg must be on virtual holiday.
>
> Yoo-hoo?
>
> Anyone else, then alive?
>
> I hope open source doesn't mean "Closed community :)"

We're not closed :)

However, this does not mean that each and every mail is answered here.

> Here I am. I want to contribute some code. But I need some help, because
> not everything can be done alone on this planet. Sometimes you need
> guidance.
>
> It's better to talk, and send people away on a search, then to ignore
> people.
>
> And all I want is some small help with i2c.

Sorry, I don't have any experience with 12c.
--
vda

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-07-15  0:05 [BK PATCH] I2C update for 2.6.8-rc1 Greg KH
  2004-07-15  0:07 ` [PATCH] " Greg KH
  2004-07-17 14:30 ` Greg (or anyone else) one small i2c question Reinder
@ 2004-08-24 21:58 ` Alex Williamson
  2004-08-24 22:04   ` Greg KH
  2 siblings, 1 reply; 32+ messages in thread
From: Alex Williamson @ 2004-08-24 21:58 UTC (permalink / raw)
  To: Greg KH; +Cc: torvalds, akpm, linux-kernel, sensors

On Wed, 2004-07-14 at 17:05 -0700, Greg KH wrote:

> <orange:fobie.net>:
>   o I2C: patch quirks.c - SMBus hidden on hp laptop

   This particular patch, along w/ the new 20040715 ACPI drop has made
my nc6000 laptop unusable.  The problem is we're exposing a device that
firmware considers hidden.  The new motherboard driver in ACPI goes out
and tries to claim resources to prevent them from being stepped on.  It
rightfully considers the hidden SMBus device a motherboard resource.
The PCI code then stumbles onto this device, sees that the BAR it's
using is unavailable and moves it somewhere else in the address space.
At this point, I lose two for the three thermal zones on the laptop
because the AML that deals with them assumes they haven't moved.

   I'm not sure what the point on un-hiding this devices it.  ACPI sets
up an OpRegion to access this device and should have exclusive access to
that region.  Letting a sensor driver poke at it may be fun, but I'd
rather not fry my laptop.  Can we drop the un-hiding of the SMBus for
this laptop (probably the nc8000 too), or is there some way to make the
ACPI motherboard driver and this quirk live together?  Thanks,

	Alex

-- 
Alex Williamson                             HP Linux & Open Source Lab


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-24 21:58 ` [BK PATCH] I2C update for 2.6.8-rc1 Alex Williamson
@ 2004-08-24 22:04   ` Greg KH
  2004-08-25  0:37     ` Linus Torvalds
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-08-24 22:04 UTC (permalink / raw)
  To: Alex Williamson; +Cc: torvalds, akpm, linux-kernel, sensors

On Tue, Aug 24, 2004 at 03:58:42PM -0600, Alex Williamson wrote:
> On Wed, 2004-07-14 at 17:05 -0700, Greg KH wrote:
> 
> > <orange:fobie.net>:
> >   o I2C: patch quirks.c - SMBus hidden on hp laptop
> 
>    This particular patch, along w/ the new 20040715 ACPI drop has made
> my nc6000 laptop unusable.  The problem is we're exposing a device that
> firmware considers hidden.  The new motherboard driver in ACPI goes out
> and tries to claim resources to prevent them from being stepped on.  It
> rightfully considers the hidden SMBus device a motherboard resource.
> The PCI code then stumbles onto this device, sees that the BAR it's
> using is unavailable and moves it somewhere else in the address space.
> At this point, I lose two for the three thermal zones on the laptop
> because the AML that deals with them assumes they haven't moved.
> 
>    I'm not sure what the point on un-hiding this devices it.  ACPI sets
> up an OpRegion to access this device and should have exclusive access to
> that region.  Letting a sensor driver poke at it may be fun, but I'd
> rather not fry my laptop.  Can we drop the un-hiding of the SMBus for
> this laptop (probably the nc8000 too), or is there some way to make the
> ACPI motherboard driver and this quirk live together?  Thanks,

See the bugzilla.kernel.org bug #3191 for more information.

If someone can come up with a patch that works for everyone, I'll be
glad to apply it.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-24 22:04   ` Greg KH
@ 2004-08-25  0:37     ` Linus Torvalds
  2004-08-25  1:38       ` Alex Williamson
  0 siblings, 1 reply; 32+ messages in thread
From: Linus Torvalds @ 2004-08-25  0:37 UTC (permalink / raw)
  To: Greg KH; +Cc: Alex Williamson, akpm, linux-kernel, sensors



On Tue, 24 Aug 2004, Greg KH wrote:
> 
> If someone can come up with a patch that works for everyone, I'll be
> glad to apply it.

Hmm.. Why exactly does the PCI layer re-locate the IO ports?

Afaik, ACPI requesting the region shouldn't matter. The 
"request_resource()" should still be happy..

Ahh. I see the problem. Because the ACPI code allocates the sub-resources, 
the request_resource thing is indeed not happy.

How about this _trivial_ change? Does that fix things for you guys? Can 
you send the /proc/ioport output if this works out for you, just so that 
we can see?

(The difference between "request_resource()" and "insert_resource()" is 
that the latter accepts pre-existing sub-resources that the firmware might 
have allocated within the resource we have..)

		Linus

===== arch/i386/pci/i386.c 1.16 vs edited =====
--- 1.16/arch/i386/pci/i386.c	2003-07-31 16:47:19 -07:00
+++ edited/arch/i386/pci/i386.c	2004-08-24 17:36:10 -07:00
@@ -142,7 +142,7 @@
 				DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
 				    r->start, r->end, r->flags, disabled, pass);
 				pr = pci_find_parent_resource(dev, r);
-				if (!pr || request_resource(pr, r) < 0) {
+				if (!pr || insert_resource(pr, r) < 0) {
 					printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, pci_name(dev));
 					/* We'll assign a new address later */
 					r->end -= r->start;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-25  0:37     ` Linus Torvalds
@ 2004-08-25  1:38       ` Alex Williamson
  2004-08-25  1:42         ` Alex Williamson
  2004-08-25  2:02         ` Linus Torvalds
  0 siblings, 2 replies; 32+ messages in thread
From: Alex Williamson @ 2004-08-25  1:38 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Greg KH, akpm, linux-kernel, sensors

On Tue, 2004-08-24 at 17:37 -0700, Linus Torvalds wrote:
> 
> On Tue, 24 Aug 2004, Greg KH wrote:
> > 
> > If someone can come up with a patch that works for everyone, I'll be
> > glad to apply it.
> 
> Hmm.. Why exactly does the PCI layer re-locate the IO ports?
> 
> Afaik, ACPI requesting the region shouldn't matter. The 
> "request_resource()" should still be happy..
> 
> Ahh. I see the problem. Because the ACPI code allocates the sub-resources, 
> the request_resource thing is indeed not happy.
> 
> How about this _trivial_ change? Does that fix things for you guys? Can 
> you send the /proc/ioport output if this works out for you, just so that 
> we can see?

   Yes, this works.  Please commit.  I still have reservations about
exposing this device (that firmware owns and we can't possibly
synchronize access to), but this is a big improvement over the unusable
state w/o this change.  Here's my /proc/ioports:

0000-001f : dma1
0020-0021 : pic1
0040-005f : timer
0060-006f : keyboard
0080-008f : dma page reg
00a0-00a1 : pic2
00c0-00df : dma2
00f0-00ff : fpu
01f0-01f7 : ide0
03c0-03df : vga+
03e8-03ef : serial
03f6-03f6 : ide0
03f8-03ff : serial
0cf8-0cff : PCI conf1
1000-107f : 0000:00:1f.0
  1000-107f : motherboard
    1000-1003 : PM1a_EVT_BLK
    1004-1005 : PM1a_CNT_BLK
    1008-100b : PM_TMR
    1010-1015 : ACPI CPU throttle
    1020-1020 : PM2_CNT_BLK
    1028-102f : GPE0_BLK
1100-113f : 0000:00:1f.0
  1100-113f : motherboard
1200-121f : motherboard
  1200-121f : 0000:00:1f.3
2000-2fff : PCI Bus #01
  2000-20ff : 0000:01:00.0
    2000-20ff : radeonfb
3000-30ff : 0000:00:1f.5
3400-34ff : 0000:00:1f.6
  3400-34ff : Intel 82801DB-ICH4 Modem - AC'97
3800-387f : 0000:00:1f.6
  3800-387f : Intel 82801DB-ICH4 Modem - Controller
3880-38bf : 0000:00:1f.5
38c0-38df : 0000:00:1d.0
  38c0-38df : uhci_hcd
38e0-38ff : 0000:00:1d.1
  38e0-38ff : uhci_hcd
3c00-3c1f : 0000:00:1d.2
  3c00-3c1f : uhci_hcd
3c20-3c2f : 0000:00:1f.1
  3c20-3c27 : ide0
  3c28-3c2f : ide1
4000-40ff : PCI CardBus #03
4400-44ff : PCI CardBus #03
4800-48ff : PCI CardBus #07
4c00-4cff : PCI CardBus #07
5000-50ff : PCI CardBus #0b
5400-54ff : PCI CardBus #0b

Thanks,

	Alex

-- 
Alex Williamson                             HP Linux & Open Source Lab


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-25  1:38       ` Alex Williamson
@ 2004-08-25  1:42         ` Alex Williamson
  2004-08-25  2:02         ` Linus Torvalds
  1 sibling, 0 replies; 32+ messages in thread
From: Alex Williamson @ 2004-08-25  1:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Greg KH, akpm, linux-kernel, sensors

On Tue, 2004-08-24 at 19:38 -0600, Alex Williamson wrote:

> 1200-121f : motherboard
>   1200-121f : 0000:00:1f.3

   BTW, this is the resource that was in conflict.  Thanks,

	Alex

-- 
Alex Williamson                             HP Linux & Open Source Lab


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-25  1:38       ` Alex Williamson
  2004-08-25  1:42         ` Alex Williamson
@ 2004-08-25  2:02         ` Linus Torvalds
  2004-08-25  6:14           ` Greg KH
  1 sibling, 1 reply; 32+ messages in thread
From: Linus Torvalds @ 2004-08-25  2:02 UTC (permalink / raw)
  To: Alex Williamson; +Cc: Greg KH, akpm, linux-kernel, sensors



On Tue, 24 Aug 2004, Alex Williamson wrote:
> > 
> > How about this _trivial_ change? Does that fix things for you guys? Can 
> > you send the /proc/ioport output if this works out for you, just so that 
> > we can see?
> 
>    Yes, this works.  Please commit.

Greg? Opinions? I'll happily commit it, since it's almost certainly bound
to be better than what we have now, but I have to admit that I could also 
see it causing confusion if two devices are set up by the BIOS to be on 
top of each other (since insert_resource() would indeed allow that).

Now, admittedly, that would be a VERY broken BIOS, and likely such a 
situation wouldn't have worked _anyway_, but you're the PCI maintainer, so 
you get to sit in the hot seat and say aye or nay.

> I still have reservations about exposing this device (that firmware owns
> and we can't possibly synchronize access to), but this is a big
> improvement over the unusable state w/o this change.

In general, it's always a good idea to expose the devices, because if you 
don't, other device drivers may end up thinking part of the addresses are 
free for the taking. Which is why we have ACPI do it's things too.


> Here's my /proc/ioports:
> 
> 1000-107f : 0000:00:1f.0
>   1000-107f : motherboard
>     1000-1003 : PM1a_EVT_BLK
>     1004-1005 : PM1a_CNT_BLK
>     1008-100b : PM_TMR
>     1010-1015 : ACPI CPU throttle
>     1020-1020 : PM2_CNT_BLK
>     1028-102f : GPE0_BLK

Nice and readable.

> 1100-113f : 0000:00:1f.0
>   1100-113f : motherboard

Same.

> 1200-121f : motherboard
>   1200-121f : 0000:00:1f.3

This is apparently the one that used to cause trouble, and you can see it 
from the nesting level: the device nests inside the description, rather 
than the other way around.

This does bring up an alternate fix: namely to do the PCI quirks 
_earlier_.

If we had done the PCI quirk handling and probing for this device _before_
ACPI populated the IO stuff, and we'd never have seen this problem. Why 
did we let ACPI come in first in the first place? Greg? Len?

The right order (I think) should be:
 - walk the existing PCI setup, do the header quirks, populate the device 
   and resource trees..
 - _than_ do the ACPI resources

The allocation of new PCI resources will happen much later - if/when the
device is actually enabled.

Or am I missing something?

		Linus

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-25  2:02         ` Linus Torvalds
@ 2004-08-25  6:14           ` Greg KH
  2004-08-25  6:36             ` Greg KH
  0 siblings, 1 reply; 32+ messages in thread
From: Greg KH @ 2004-08-25  6:14 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Alex Williamson, akpm, linux-kernel, sensors

On Tue, Aug 24, 2004 at 07:02:47PM -0700, Linus Torvalds wrote:
> On Tue, 24 Aug 2004, Alex Williamson wrote:
> > > How about this _trivial_ change? Does that fix things for you guys? Can 
> > > you send the /proc/ioport output if this works out for you, just so that 
> > > we can see?
> > 
> >    Yes, this works.  Please commit.
> 
> Greg? Opinions? I'll happily commit it, since it's almost certainly bound
> to be better than what we have now, but I have to admit that I could also 
> see it causing confusion if two devices are set up by the BIOS to be on 
> top of each other (since insert_resource() would indeed allow that).
> 
> Now, admittedly, that would be a VERY broken BIOS, and likely such a 
> situation wouldn't have worked _anyway_, but you're the PCI maintainer, so 
> you get to sit in the hot seat and say aye or nay.

It looks correct to me, please apply it.  If it breaks people's boxes
that used to work, I'm sure I'll hear about it :)

> > 1200-121f : motherboard
> >   1200-121f : 0000:00:1f.3
> 
> This is apparently the one that used to cause trouble, and you can see it 
> from the nesting level: the device nests inside the description, rather 
> than the other way around.
> 
> This does bring up an alternate fix: namely to do the PCI quirks 
> _earlier_.
> 
> If we had done the PCI quirk handling and probing for this device _before_
> ACPI populated the IO stuff, and we'd never have seen this problem. Why 
> did we let ACPI come in first in the first place? Greg? Len?

I don't really know, I think that happened before both Len and I took
over this code base.  I don't have a problem with doing it in this
order, but I'm sure there's some ACPI issue that I'm not aware of that
needs to be run first.

> The right order (I think) should be:
>  - walk the existing PCI setup, do the header quirks, populate the device 
>    and resource trees..
>  - _than_ do the ACPI resources

For a box such as this one, it would make sense to do it in this order,
I agree.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [BK PATCH] I2C update for 2.6.8-rc1
  2004-08-25  6:14           ` Greg KH
@ 2004-08-25  6:36             ` Greg KH
  0 siblings, 0 replies; 32+ messages in thread
From: Greg KH @ 2004-08-25  6:36 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Alex Williamson, akpm, linux-kernel, sensors

On Tue, Aug 24, 2004 at 11:14:45PM -0700, Greg KH wrote:
> On Tue, Aug 24, 2004 at 07:02:47PM -0700, Linus Torvalds wrote:
> > Now, admittedly, that would be a VERY broken BIOS, and likely such a 
> > situation wouldn't have worked _anyway_, but you're the PCI maintainer, so 
> > you get to sit in the hot seat and say aye or nay.
> 
> It looks correct to me, please apply it.  If it breaks people's boxes
> that used to work, I'm sure I'll hear about it :)

It didn't break my finicky little laptop, so it's fine with me.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Greg (or anyone else) one small i2c question.
  2004-07-17 14:30 ` Greg (or anyone else) one small i2c question Reinder
  2004-07-30  5:40   ` --- " Reinder
@ 2004-08-25  6:44   ` Greg KH
  1 sibling, 0 replies; 32+ messages in thread
From: Greg KH @ 2004-08-25  6:44 UTC (permalink / raw)
  To: Reinder; +Cc: linux-kernel

On Sat, Jul 17, 2004 at 04:30:21PM +0200, Reinder wrote:
> Greg,

Hm, try asking this on the sensors mailing list, they should be able to
help you out better than I about this topic.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2004-08-25  7:01 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-07-15  0:05 [BK PATCH] I2C update for 2.6.8-rc1 Greg KH
2004-07-15  0:07 ` [PATCH] " Greg KH
2004-07-15  0:07   ` Greg KH
2004-07-15  0:07     ` Greg KH
2004-07-15  0:07       ` Greg KH
2004-07-15  0:07         ` Greg KH
2004-07-15  0:07           ` Greg KH
2004-07-15  0:07             ` Greg KH
2004-07-15  0:07               ` Greg KH
2004-07-15  0:07                 ` Greg KH
2004-07-15  0:07                   ` Greg KH
2004-07-15  0:07                     ` Greg KH
2004-07-15  0:07                       ` Greg KH
2004-07-15  0:07                         ` Greg KH
2004-07-15  0:07                           ` Greg KH
2004-07-15  0:07                             ` Greg KH
2004-07-16 17:07                         ` Pavel Machek
2004-07-16 17:17                           ` Greg KH
2004-07-16 17:39                             ` Bob Riegelmann
2004-07-16 18:19                             ` Adam Kropelin
2004-07-17 14:30 ` Greg (or anyone else) one small i2c question Reinder
2004-07-30  5:40   ` --- " Reinder
2004-07-30  6:30     ` Denis Vlasenko
2004-08-25  6:44   ` Greg " Greg KH
2004-08-24 21:58 ` [BK PATCH] I2C update for 2.6.8-rc1 Alex Williamson
2004-08-24 22:04   ` Greg KH
2004-08-25  0:37     ` Linus Torvalds
2004-08-25  1:38       ` Alex Williamson
2004-08-25  1:42         ` Alex Williamson
2004-08-25  2:02         ` Linus Torvalds
2004-08-25  6:14           ` Greg KH
2004-08-25  6:36             ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox