All of lore.kernel.org
 help / color / mirror / Atom feed
From: corentin.labbe@geomatys.fr (corentin.labbe)
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] [PATCH] Add MAX6650 support
Date: Thu, 01 Mar 2007 17:34:54 +0000	[thread overview]
Message-ID: <45E70EBE.40308@geomatys.fr> (raw)
In-Reply-To: <200701171359.46334.hjk@linutronix.de>

Hans-J?rgen Koch a ?crit :
> Am Sonntag, 11. Februar 2007 17:22 schrieb Hans-J?rgen Koch:
>> Am Sonntag 11 Februar 2007 16:44 schrieb corentin.labbe:
>>> Hello
>>>
>>> i saw some problems in your code:
>>>
>>> On line 141 you excess the 80 characters width
>>> + static ssize_t get_fan(struct device *dev, struct
>>> device_attribute *devattr, char *buf, int nr)
>>>
>>>
>>> On the comment you have written "MAX6650 also" instead of "MAX6551
>>> also" * This module has only been tested with the MAX6650 chip. It
>>> should * work with the MAX6650 also, though with reduced
>>> functionality. It * does not distinguish max6650 and max6651 chips.
>>>
>>>
>>> You must use dev_dbg instead of #ifdef debug
>>>
>>>
>>> On line 535 the while(0) is useless
>>> erase the max6650_init_client function
>>> +static void max6650_init_client(struct i2c_client *client)
>>> +{
>>> +	/* Nothing to do here - assume the BIOS has initialized the chip
>>> */ +	while(0)
>>> +	{
>>> +		;
>>> +	}
>>> +}
>>>
>>>
>>> Don't use down() up()
>>> replace with mutex
>>>
>>> On line 553
>>> 	MAX6650_REG_TACH0, MAX6650_REG_TACH1,
>>> 	MAX6650_REG_TACH2, MAX6650_REG_TACH3
>>> 	one per line
>>> 	"," at the end
>>> like
>>> static const u8 tach_reg[] >>> {
>>> 	MAX6650_REG_TACH0,
>>> 	MAX6650_REG_TACH1,
>>> 	MAX6650_REG_TACH2,
>>> 	MAX6650_REG_TACH3,
>>> };
>>>
>>>
>>> Why use max6650_read_value and max6650_write_value?
>>> replace with i2c_smbus_write_byte_data and i2c_smbus_read_byte_data
>>> directly
>>>
>>> Check error return code of i2c_detach_client(client);
>>> like
>>> if ((err = i2c_detach_client(client)))
>>> 	return err;
>>>
>>>
>>> Don't use many device_create_file
>>> use sysfs_create_group
>> Thanks for your review, I'll look at the code again and post an
>> updated patch soon.
>>
> 
> Hi,
> here's the promised patch with these issues hopefully solved. I tested the 
> module on a Kontron CPX board, with a vanilla 2.6.20 kernel, it seems to work 
> fine.
> 
> Thanks again for the review,
> 
> Hans
>  
> 
> 
> ------------------------------------------------------------------------
> 
> Index: linux-2.6.20/Documentation/hwmon/max6650
> =================================> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.20/Documentation/hwmon/max6650	2007-02-12 14:40:26.000000000 +0100
> @@ -0,0 +1,33 @@
> +Kernel driver max6650
> +==========> +
> +Supported chips:
> +  * Maxim 6650 / 6651
> +    Prefix: 'w83792d'
> +    Addresses scanned: I2C 0x1b, 0x1f, 0x48, 0x4b
> +    Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
> +
> +Authors:
> +    John Morris <john.morris at spirentcom.com>
> +    Claus Gindhart <claus.gindhart at kontron.com>
> +    Hans J. Koch <hjk at linutronix.de>
> +
> +Description
> +-----------
> +
> +This driver implements support for the Maxim 6650/6651
> +
> +The 2 devices are very similar, bit the Maxim 6550 has a reduced feature
> +set, e.g. only one fan-input, instead of 4 for the 6651.
> +
> +The driver is not able to distinguish between the 2 devices.
> +
> +The driver provides the following sensor accesses
> +
> +fan0   ro     fan tachometer speed in RPM
> +fan1   ro     "
> +fan2   ro     "
> +fan3   ro     "
> +config ro     contents of the config register (for debugging)
> +count  ro     tachometer count time in milliseconds
> +speed  rw     fan speed adjustment value
> Index: linux-2.6.20/drivers/hwmon/Kconfig
> =================================> --- linux-2.6.20.orig/drivers/hwmon/Kconfig	2007-02-04 19:44:54.000000000 +0100
> +++ linux-2.6.20/drivers/hwmon/Kconfig	2007-02-12 09:01:25.000000000 +0100
> @@ -364,6 +364,16 @@
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called max1619.
>  
> +config SENSORS_MAX6650
> +	tristate "Maxim MAX6650 sensor chip"
> +	depends on HWMON && I2C && EXPERIMENTAL
> +	help
> +	  If you say yes here you get support for the MAX6650 / MAX6651
> +	  sensor chips.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called max6650.
> +
>  config SENSORS_PC87360
>  	tristate "National Semiconductor PC87360 family"
>  	depends on HWMON && I2C && EXPERIMENTAL
> Index: linux-2.6.20/drivers/hwmon/Makefile
> =================================> --- linux-2.6.20.orig/drivers/hwmon/Makefile	2007-02-04 19:44:54.000000000 +0100
> +++ linux-2.6.20/drivers/hwmon/Makefile	2007-02-12 09:01:25.000000000 +0100
> @@ -42,6 +42,7 @@
>  obj-$(CONFIG_SENSORS_LM90)	+= lm90.o
>  obj-$(CONFIG_SENSORS_LM92)	+= lm92.o
>  obj-$(CONFIG_SENSORS_MAX1619)	+= max1619.o
> +obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
>  obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
>  obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
>  obj-$(CONFIG_SENSORS_SIS5595)	+= sis5595.o
> Index: linux-2.6.20/drivers/hwmon/max6650.c
> =================================> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.20/drivers/hwmon/max6650.c	2007-02-12 14:34:50.000000000 +0100
> @@ -0,0 +1,558 @@
> +/*
> + * max6650.c - Part of lm_sensors, Linux kernel modules for hardware
> + *             monitoring.
> + *
> + * Author: John Morris <john.morris at spirentcom.com>
> + * Copyright (c) 2003 Spirent Communications
> + *
> + * This module has only been tested with the MAX6650 chip. It should
> + * also work with the MAX6651, though with reduced functionality. It
> + * does not distinguish max6650 and max6651 chips.
> + *
> + * Tha datasheet was last seen at:
> + *
> + *        http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
> + *
> + * Ported to Linux 2.6 by Claus Gindhart <claus.gindhart at kontron.com>
> + * Some cleanup done by Hans J. Koch <hjk at linutronix.de>
> + *
> + * 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/jiffies.h>
> +#include <linux/i2c.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +
> +#ifndef I2C_DRIVERID_MAX6650
> +#define I2C_DRIVERID_MAX6650	1044
> +#endif
> +
> +/* HW-related: Set this to 1 for 12V and 0 for 5V configuration */
> +static const int voltage_12V=0;
> +
> +/*
> + * Addresses to scan. There are four disjoint possibilities, by pin config.
> + */
> +
> +static unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, I2C_CLIENT_END};
> +
> +/*
> + * Insmod parameters
> + */
> +
> +I2C_CLIENT_INSMOD_1(max6650);
> +
> +/*
> + * MAX 6650/6651 registers
> + */
> +
> +#define MAX6650_REG_SPEED       0x00
> +#define MAX6650_REG_CONFIG      0x02
> +#define MAX6650_REG_GPIO_DEF    0x04
> +#define MAX6650_REG_DAC         0x06
> +#define MAX6650_REG_ALARM_EN    0x08
> +#define MAX6650_REG_ALARM       0x0A
> +#define MAX6650_REG_TACH0       0x0C
> +#define MAX6650_REG_TACH1       0x0E
> +#define MAX6650_REG_TACH2       0x10
> +#define MAX6650_REG_TACH3       0x12
> +#define MAX6650_REG_GPIO_STAT   0x14
> +#define MAX6650_REG_COUNT       0x16
> +
> +
> +/*
> + * Config register bits
> + */
> +
> +#define MAX6650_CFG_V12           	0x08
> +#define MAX6650_CFG_MODE_MASK           0x30
> +#define MAX6650_CFG_MODE_ON             0x00
> +#define MAX6650_CFG_MODE_OFF            0x10
> +#define MAX6650_CFG_MODE_CLOSED_LOOP    0x20
> +#define MAX6650_CFG_MODE_OPEN_LOOP      0x30
> +
> +#define MAX6650_INT_CLK 254000  /* Default clock speed - 254 kHz */
> +
> +/* Minimum and maximum values of the FAN-RPM */
> +#define FAN_RPM_MIN 240
> +#define FAN_RPM_MAX 30000
> +
> +#define DIV_FROM_REG(reg) (1 << (reg & 7))
> +
> +static int max6650_attach_adapter(struct i2c_adapter *adapter);
> +static int max6650_detect(struct i2c_adapter *adapter, int address, int kind);
> +static void max6650_init_client(struct i2c_client *client);
> +static int max6650_detach_client(struct i2c_client *client);
> +static struct max6650_data *max6650_update_device(struct device *dev);
> +
> +/*
> + * Driver data (common to all clients)
> + */
> +
> +static struct i2c_driver max6650_driver = {
> +	.driver = {
> +		.name	= "max6650",
> +	},
> +	.id             = I2C_DRIVERID_MAX6650,
> +	.attach_adapter = max6650_attach_adapter,
> +	.detach_client  = max6650_detach_client
> +};
> +
> +/*
> + * Client data (each client gets its own)
> + */
> +
> +struct max6650_data
> +{
> +	struct i2c_client client;
> +	struct class_device *class_dev;
> +	struct mutex update_lock;
> +	char valid; /* zero until following fields are valid */
> +	unsigned long last_updated; /* in jiffies */
> +
> +	/* register values */
> +	u8 speed;
> +	u8 config;
> +	u8 tach[4];
> +	u8 count;
> +};
> +
> +static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
> +		       char *buf, int nr)
> +{
> +	struct max6650_data *data = max6650_update_device(dev);
> +
> +	/*
> +	* Calculation details:
> +	*
> +	* Each tachometer counts over an interval given by the "count"
> +	* register (0.25, 0.5, 1 or 2 seconds). This module assumes
> +	* that the fans produce two pulses per revolution (this seems
> +	* to be the most common).
> +	*/
> +
> +	int rpm = ((data->tach[nr] * 120) / DIV_FROM_REG(data->count));
> +	return sprintf(buf, "%d\n", rpm);
> +}
> +
> +static ssize_t get_fan0(struct device *dev, struct device_attribute *devattr,
> +			char *buf)
> +{
> +	return get_fan(dev,devattr,buf,0);
> +}
> +
> +static ssize_t get_fan1(struct device *dev, struct device_attribute *devattr,
> +			char *buf)
> +{
> +	return get_fan(dev,devattr,buf,1);
> +}
> +
> +static ssize_t get_fan2(struct device *dev, struct device_attribute *devattr,
> +			char *buf)
> +{
> +	return get_fan(dev,devattr,buf,2);
> +}
> +
> +static ssize_t get_fan3(struct device *dev, struct device_attribute *devattr,
> +			char *buf)
> +{
> +	return get_fan(dev,devattr,buf,3);
> +}
> +
> +/* Show values of the config register for debugging purposes
> + */
> +
> +static ssize_t get_config(struct device *dev, struct device_attribute *devattr,
> +			  char *buf)
> +{
> +	struct max6650_data *data = max6650_update_device(dev);
> +
> +	return sprintf(buf, "mode: %s%s%s%s, voltage:%s prescale: %d\n",
> +		((data->config & MAX6650_CFG_MODE_MASK) =
> +		  MAX6650_CFG_MODE_ON) ? "On" : "",
> +		((data->config & MAX6650_CFG_MODE_MASK) =
> +		  MAX6650_CFG_MODE_OFF) ? "Off" : "",
> +		((data->config & MAX6650_CFG_MODE_MASK) =
> +		  MAX6650_CFG_MODE_CLOSED_LOOP) ? "closed loop" : "",
> +		((data->config & MAX6650_CFG_MODE_MASK) =
> +		  MAX6650_CFG_MODE_OPEN_LOOP) ? "open Loop" : "",
> +		(data->config & MAX6650_CFG_V12) ? "12V" : "5V",
> +		DIV_FROM_REG(data->config)
> +		);
> +}
> +
> +/* Returns count time in ms */
> +static ssize_t get_count(struct device *dev, struct device_attribute *devattr,
> +			 char *buf)
> +{
> +	struct max6650_data *data = max6650_update_device(dev);
> +
> +	return sprintf(buf, "%d\n", DIV_FROM_REG(data->count) * 250 );
> +}
> +
> +/*
> + * Set the fan speed to the specified RPM (or read back the RPM setting).
> + *
> + * The MAX6650/1 will automatically control fan speed when in closed loop
> + * mode.
> + *
> + * Assumptions:
> + *
> + * 1) The MAX6650/1 is running from its internal 254kHz clock (perhaps
> + *    this should be made a module parameter).
> + *
> + * 2) The prescaler (low three bits of the config register) has already
> + *    been set to an appropriate value.
> + *
> + * The relevant equations are given on pages 21 and 22 of the datasheet.
> + *
> + * From the datasheet, the relevant equation when in regulation is:
> + *
> + *    [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE
> + *
> + * where:
> + *
> + *    fCLK is the oscillator frequency (either the 254kHz internal
> + *         oscillator or the externally applied clock)
> + *
> + *    KTACH is the value in the speed register
> + *
> + *    FanSpeed is the speed of the fan in rps
> + *
> + *    KSCALE is the prescaler value (1, 2, 4, 8, or 16)
> + *
> + * When reading, we need to solve for FanSpeed. When writing, we need to
> + * solve for KTACH.
> + *
> + * Note: this tachometer is completely separate from the tachometers
> + * used to measure the fan speeds. Only one fan's speed (fan1) is
> + * controlled.
> + */
> +
> +static ssize_t get_speed(struct device *dev, struct device_attribute *devattr,
> +			 char *buf)
> +{
> +	struct max6650_data *data = max6650_update_device(dev);
> +	int kscale, ktach, fclk, rpm;
> +
> +	/*
> +	* Use the datasheet equation:
> +	*
> +	*    FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
> +	*
> +	* then multiply by 60 to give rpm.
> +	*/
> +
> +	kscale = DIV_FROM_REG(data->config);
> +	ktach  = data->speed;
> +	fclk   = MAX6650_INT_CLK;
> +	rpm    = 60 * kscale * fclk / (256 * (ktach + 1));
> +	return sprintf(buf, "%d\n", rpm);
> +}
> +
> +static ssize_t set_speed(struct device *dev, struct device_attribute *devattr,
> +			 const char *buf, size_t count)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct max6650_data *data = i2c_get_clientdata(client);
> +	int rpm = simple_strtoul(buf, NULL, 10);
> +	int kscale, ktach, fclk;
> +
> +	dev_dbg(dev, "%s called, rpm=%d\n", __FUNCTION__, rpm);
> +
> +	mutex_lock(&data->update_lock);
> +	if (rpm = 0)
> +	{
> +		/* Switch totally off */
> +		data->speed  = 0;
> +		data->config = (data->config & ~MAX6650_CFG_MODE_MASK)
> +				| MAX6650_CFG_MODE_OFF;
> +	}
> +	else
> +	{
> +		rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
> +
> +		/*
> +		* Divide the required speed by 60 to get from rpm to rps, then
> +		* use the datasheet equation:
> +		*
> +		*     KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
> +		*/
> +
> +		kscale = DIV_FROM_REG(data->config);
> +		fclk   = MAX6650_INT_CLK;
> +		ktach  = ((fclk * kscale) / (256 * rpm / 60)) - 1;
> +
> +		data->speed  = ktach;
> +		data->config = (data->config & ~MAX6650_CFG_MODE_MASK)
> +				| MAX6650_CFG_MODE_CLOSED_LOOP;
> +	}
> +	i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config);
> +	i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED,  data->speed);
> +
> +	mutex_unlock(&data->update_lock);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(fan0, S_IRUGO, get_fan0, NULL);
> +static DEVICE_ATTR(fan1, S_IRUGO, get_fan1, NULL);
> +static DEVICE_ATTR(fan2, S_IRUGO, get_fan2, NULL);
> +static DEVICE_ATTR(fan3, S_IRUGO, get_fan3, NULL);
> +static DEVICE_ATTR(count, S_IRUGO, get_count, NULL);
> +static DEVICE_ATTR(config, S_IRUGO, get_config, NULL);
> +static DEVICE_ATTR(speed, S_IWUSR | S_IRUGO, get_speed, set_speed);
> +
> +static struct attribute *max6650_attrs[] = {
> +	&dev_attr_fan0.attr,
> +	&dev_attr_fan1.attr,
> +	&dev_attr_fan2.attr,
> +	&dev_attr_fan3.attr,
> +	&dev_attr_count.attr,
> +	&dev_attr_config.attr,
> +	&dev_attr_speed.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group max6650_attr_grp = {
> +	.attrs = max6650_attrs,
> +};
> +
> +/*
> + * Real code
> + */
> +
> +static int max6650_attach_adapter(struct i2c_adapter *adapter)
> +{
> +	dev_dbg(adapter->dev, "max6650_attach_adapter called for %s\n",
> +		(char*)&adapter->name);
> +
> +	if (!(adapter->class & I2C_CLASS_HWMON)) {
> +		dev_dbg(adapter->dev,
> +			"FATAL: max6650_attach_adapter class HWMON not set\n");
> +		return 0;
> +	}
> +
> +	return i2c_probe(adapter, &addr_data, max6650_detect);
> +}
> +
> +/*
> + * The following function does more than just detection. If detection
> + * succeeds, it also registers the new chip.
> + */
> +
> +static int max6650_detect(struct i2c_adapter *adapter, int address, int kind)
> +{
> +	struct i2c_client *new_client;
> +	struct max6650_data *data;
> +	int err = -ENODEV;
> +	const char *name = "";
> +
> +	dev_dbg(adapter->dev, "max6650_detect called, kind = %d\n",kind);
> +
> +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
> +		dev_dbg(adapter->dev, "MAX6650: I2C bus doesn't support byte"
> +			"read mode, skipping.\n");
> +		return 0;
> +	}
> +
> +	if (!(data = kzalloc(sizeof(struct max6650_data), GFP_KERNEL))) {
> +		printk("MAX6650: Out of memory in max6650_detect "
> +			"(new_client).\n");
> +		return -ENOMEM;
> +	}
> +
> +	/*
> +	 * The common I2C client data is placed right before the
> +	 * max6650-specific data. The max6650-specific data is pointed to by
> +	 * the data field from the I2C client data.
> +	 */
> +
> +	new_client = &data->client;
> +	i2c_set_clientdata(new_client, data);
> +	new_client->addr = address;
> +	new_client->adapter = adapter;
> +	new_client->driver = &max6650_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 (actually there is only
> +	 * one possible kind of chip for now, max6650). 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.
> +	 *
> +	 * Currently I can find no way to distinguish between a MAX6650 and
> +	 * a MAX6651. This driver has only been tried on the latter.
> +	 */
> +
> +	if ((kind < 0)&&
> +	   (  (i2c_smbus_read_byte_data(new_client,MAX6650_REG_CONFIG) & 0xC0)
> +	    ||(i2c_smbus_read_byte_data(new_client,MAX6650_REG_GPIO_STAT)& 0xE0)
> +	    ||(i2c_smbus_read_byte_data(new_client,MAX6650_REG_ALARM_EN) & 0xE0)
> +	    ||(i2c_smbus_read_byte_data(new_client,MAX6650_REG_ALARM) & 0xE0)
> +	    ||(i2c_smbus_read_byte_data(new_client,MAX6650_REG_COUNT) & 0xFC)))
> +	{
> +		dev_dbg(adapter->dev, "max6650.o: max6650 detection failed"
> +			"at 0x%02x.\n", address);
> +
> +		goto err_free;
> +        }
> +
> +	/* Configure HW-related voltage setting */
> +	if (voltage_12V) {
> +		i2c_smbus_write_byte_data(new_client, MAX6650_REG_CONFIG,
> +			i2c_smbus_read_byte_data(new_client, MAX6650_REG_CONFIG)
> +			| MAX6650_CFG_V12);
> +	}
> +	else {
> +		i2c_smbus_write_byte_data(new_client, MAX6650_REG_CONFIG,
> +			i2c_smbus_read_byte_data(new_client, MAX6650_REG_CONFIG)
> +			& ~MAX6650_CFG_V12);
> +	}
> +
> +	if (kind <= 0) {
> +		kind = max6650;
> +	}
> +
> +	if (kind = max6650) {
> +		name = "max6650";
> +		printk("MAX6650: Chip found at 0x%02x.\n", address);
> +	}
> +	else {
> +		printk("MAX6650: Unsupported chip.\n");
> +		goto err_free;
> +	}
> +
> +	/*
> +	 * OK, we got a valid chip so we can fill in the remaining client
> +	 * fields.
> +	 */
> +
> +	strlcpy(new_client->name, name, I2C_NAME_SIZE);
> +	data->valid = 0;
> +	mutex_init(&data->update_lock);
> +
> +	/*
> +	 * Tell the I2C layer a new client has arrived.
> +	 */
> +
> +	if ((err = i2c_attach_client(new_client))) {
> +		dev_dbg(adapter->dev, "max6650.o: Failed attaching client.\n");
> +		goto err_free;
> +	}
> +
> +	/*
> +	 * Initialize the max6650 chip
> +	 */
> +	max6650_init_client(new_client);
> +
> +	/* Register sysfs hooks */
> +	data->class_dev = hwmon_device_register(&new_client->dev);
> +	if (IS_ERR(data->class_dev)) {
> +		err = PTR_ERR(data->class_dev);
> +		goto err_detach;
> +	}
> +
> +	err = sysfs_create_group(&new_client->dev.kobj, &max6650_attr_grp);
> +	if (!err)
> +		return 0;
> +
> +	dev_err(&new_client->dev, "error creating sysfs files (%d)\n", err);
> +	hwmon_device_unregister(data->class_dev);
> +err_detach:
> +	i2c_detach_client(new_client);
> +err_free:
> +	kfree(data);
> +	return err;
> +}
> +
> +static int max6650_detach_client(struct i2c_client *client)
> +{
> +	struct max6650_data *data = i2c_get_clientdata(client);
> +	int err;
> +	hwmon_device_unregister(data->class_dev);
> +	err = i2c_detach_client(client);
> +	sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp);
> +	kfree(data);
> +	return err;
> +}
> +
> +static void max6650_init_client(struct i2c_client *client)
> +{
> +	/* Nothing to do here - assume the BIOS has initialized the chip */
> +}
> +
> +static const u8 tach_reg[] > +{
> +	MAX6650_REG_TACH0,
> +	MAX6650_REG_TACH1,
> +	MAX6650_REG_TACH2,
> +	MAX6650_REG_TACH3,
> +};
> +
> +static struct max6650_data *max6650_update_device(struct device *dev)
> +{
> +	int i;
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct max6650_data *data = i2c_get_clientdata(client);
> +
> +	mutex_lock(&data->update_lock);
> +
> +	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
> +		data->speed = i2c_smbus_read_byte_data(client,
> +			 			       MAX6650_REG_SPEED);
> +		data->config = i2c_smbus_read_byte_data(client,
> +							MAX6650_REG_CONFIG);
> +		for (i = 0; i < 4; i++) {
> +			data->tach[i] = i2c_smbus_read_byte_data(client,
> +								 tach_reg[i]);
> +		}
> +		data->count = i2c_smbus_read_byte_data (client,
> +							MAX6650_REG_COUNT);
> +		data->last_updated = jiffies;
> +		data->valid = 1;
> +	}
> +
> +	mutex_unlock(&data->update_lock);
> +
> +	return data;
> +}
> +
> +static int __init sensors_max6650_init(void)
> +{
> +	return i2c_add_driver(&max6650_driver);
> +}
> +
> +static void __exit sensors_max6650_exit(void)
> +{
> +	i2c_del_driver(&max6650_driver);
> +}
> +
> +MODULE_AUTHOR("john.morris at spirentcom.com");
> +MODULE_DESCRIPTION("max6650 sensor driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(sensors_max6650_init);
> +module_exit(sensors_max6650_exit);
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> lm-sensors mailing list
> lm-sensors at lm-sensors.org
> http://lists.lm-sensors.org/mailman/listinfo/lm-sensors


hello

Instead of declaring four static DEVICE_ATTR(fan1, S_IRUGO, get_fan1, NULL);
I'm suggest you to use
static SENSOR_DEVICE_ATTR(fan1, S_IRUGO, get_fan, NULL,1);
static SENSOR_DEVICE_ATTR(fan2, S_IRUGO, get_fan, NULL,2);
etc....

secondly in /Documentation/hwmon/max6650 you have a wrong prefix
+Supported chips:
 > +  * Maxim 6650 / 6651
 > +    Prefix: 'w83792d'

and 2 typos errors
(but and not bit)
-> bit the Maxim 6550 has a reduced feature

6550 not 6650
 > +  * Maxim 6650 / 6651
 > +    Prefix: 'w83792d'



  parent reply	other threads:[~2007-03-01 17:34 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-01-17 12:59 [lm-sensors] [PATCH] Add MAX6650 support Hans-Jürgen Koch
2007-01-22  8:36 ` Jean Delvare
2007-02-11 15:44 ` corentin.labbe
2007-02-11 16:22 ` Hans-Jürgen Koch
2007-02-12 13:48 ` Hans-Jürgen Koch
2007-02-27 15:21 ` [lm-sensors] " Matej Kenda
2007-02-27 15:35 ` [lm-sensors] " Jean Delvare
2007-02-27 16:17 ` Hans-Jürgen Koch
2007-02-27 16:29 ` Matej Kenda
2007-02-27 17:03 ` Matej Kenda
2007-02-27 17:13 ` Hans-Jürgen Koch
2007-02-27 18:07 ` Jean Delvare
2007-02-27 19:58 ` Hans-Jürgen Koch
2007-02-28 11:44 ` Hans-Jürgen Koch
2007-02-28 15:57 ` Matej Kenda
2007-02-28 16:08 ` Hans-Jürgen Koch
2007-03-01  7:15 ` Jean Delvare
2007-03-01  7:34 ` Jean Delvare
2007-03-01 10:11 ` Hans-Jürgen Koch
2007-03-01 17:34 ` corentin.labbe [this message]
2007-03-02 11:32 ` Jean Delvare
2007-03-05 11:34 ` Hans-Jürgen Koch
2007-03-11 14:40 ` Jean Delvare
2007-03-11 15:21 ` Jean Delvare
2007-03-11 21:08 ` Hans-Jürgen Koch
2007-03-12 13:50 ` Jean Delvare
2007-03-12 14:44 ` Hans-Jürgen Koch
2007-03-12 16:49 ` Jean Delvare
2007-03-13 13:25 ` Hans-Jürgen Koch
2007-03-15 20:10 ` Jean Delvare
2007-03-16 16:44 ` Hans-Jürgen Koch
2007-03-16 19:17 ` Jean Delvare
2007-03-16 21:07 ` Hans-Jürgen Koch
2007-03-16 21:19 ` Hans-Jürgen Koch
2007-03-16 21:19 ` Thomas Gleixner
2007-03-16 21:33 ` Thomas Gleixner
2007-03-17 13:39 ` Jean Delvare
2007-03-17 14:23 ` Hans-Jürgen Koch

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=45E70EBE.40308@geomatys.fr \
    --to=corentin.labbe@geomatys.fr \
    --cc=lm-sensors@vger.kernel.org \
    /path/to/YOUR_REPLY

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

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