[PATCH 2.6.12-rc4] max6875: new driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

Please see my comments in code. I'm not very familar with kobject stuff
but generaly it looks OK to me.

I hope it helps,
regards

Rudolf

> The MAX6875 is an EEPROM programmable, quad power supply
> sequences/supervisior.
> It is connected via the i2c bus.
> The driver also supports the MAX6874, as the interface is identical.
>  
> It's probably not found on any standard motherboard, but it's pretty
> handy for custom embedded stuff.
> 
> We use it for power supply sequencing at power up, as a watchdog timer,
> and for serial & model number storage.
> 
> The driver exposes the 2 banks of EEPROM (configuration and
> user-defined) and the bank of configuration registers.
> 
> Signed-off-by: Ben Gardner <bgardner at wabtec.com>

> diff -urEbBN linux-2.6.12-rc4-clean/drivers/i2c/chips/Kconfig linux-2.6.12-rc4-max6875/drivers/i2c/chips/Kconfig
> --- linux-2.6.12-rc4-clean/drivers/i2c/chips/Kconfig	2005-05-10 09:46:20.000000000 -0500
> +++ linux-2.6.12-rc4-max6875/drivers/i2c/chips/Kconfig	2005-05-31 12:42:46.000000000 -0500

Again due to sysfs change you need to use -mm- and add one more sysfs param.


> @@ -388,6 +388,18 @@
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called eeprom.
>  
> +config SENSORS_MAX6875
> +	tristate "MAXIM MAX6875 Power supply supervisor"
> +	depends on I2C && EXPERIMENTAL
> +	help
> +	  If you say yes here you get support for the MAX6875
> +	  EEPROM-Programmable, Hex/Quad, Power-Suppy Sequencers/Supervisors.
> +	  
> +          This provides a interface to program the user and config EEPROMs.
> +	  
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called max6875.
> +
>  config SENSORS_PCF8574
>  	tristate "Philips PCF8574 and PCF8574A"
>  	depends on I2C && EXPERIMENTAL
> diff -urEbBN linux-2.6.12-rc4-clean/drivers/i2c/chips/Makefile linux-2.6.12-rc4-max6875/drivers/i2c/chips/Makefile
> --- linux-2.6.12-rc4-clean/drivers/i2c/chips/Makefile	2005-05-10 09:46:20.000000000 -0500
> +++ linux-2.6.12-rc4-max6875/drivers/i2c/chips/Makefile	2005-05-31 12:42:06.000000000 -0500
> @@ -30,6 +30,7 @@
>  obj-$(CONFIG_SENSORS_LM90)	+= lm90.o
>  obj-$(CONFIG_SENSORS_LM92)	+= lm92.o
>  obj-$(CONFIG_SENSORS_MAX1619)	+= max1619.o
> +obj-$(CONFIG_SENSORS_MAX6875)	+= max6875.o
>  obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
>  obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
>  obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
> diff -urEbBN linux-2.6.12-rc4-clean/drivers/i2c/chips/max6875.c linux-2.6.12-rc4-max6875/drivers/i2c/chips/max6875.c
> --- linux-2.6.12-rc4-clean/drivers/i2c/chips/max6875.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.12-rc4-max6875/drivers/i2c/chips/max6875.c	2005-05-31 13:55:01.338843587 -0500
> @@ -0,0 +1,461 @@
> +/*
> +    max6875.c - driver for MAX6874/MAX6875
> +    
> +    Copyright 2005 Ben Gardner <bgardner at wabtec.com>
(C) is missing

> +    
> +    Based on i2c/chips/eeprom.c
> +    
> +    The MAX6875 has two EEPROM sections: config and user.
> +    At reset, the config EEPROM is read into the registers.
> +    
> +    This driver make 3 binary files available in sysfs: 
> +      reg_config    - direct access to the registers
> +      eeprom_config - acesses configuration eeprom space
> +      eeprom_user   - free for application use
> +      
> +    In our application, we put device serial & model numbers in user eeprom.
> +    
> +    Notes:
> +      1) The datasheet says that register 0x44 / EEPROM 0x8044 should NOT
> +         be overwritten, so the driver explicitly prevents that.
> +      2) It's a good idea to keep the config (0x45) locked in config EEPROM.
> +         You can temporarily enable config writes by changing register 0x45.
> +
> +    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; version 2 of the License.
> +*/
> +
> +#include <linux/config.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-sensor.h>
> +
> +/* Addresses to scan */
> +static unsigned short normal_i2c[] = { 0x50, 0x52, I2C_CLIENT_END};

This is quite unbalanced either remove first space or add to last.

> +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END};

This is quite unbalanced either remove first space or add to last.

> +
> +/* Insmod parameters */
> +SENSORS_INSMOD_1(max6875);
> +
> +/* this param will prevent 'accidental' writes to the eeprom */
> +static int allow_write = 0;
> +module_param(allow_write, int, 0);
> +MODULE_PARM_DESC(allow_write,
> +		 "Enable write access:\n"
> +		 "*0: Read only\n"
> +		 " 1: Read/Write access");
> +
> +/* The MAX6875 can only read/write 16 bytes at a time */
> +#define SLICE_SIZE			16
> +#define SLICE_BITS			4
> +
> +/* CONFIG EEPROM is at addresses 0x8000 - 0x8045, registers are at 0 - 0x45 */
> +#define CONFIG_EEPROM_BASE		0x8000
> +#define CONFIG_EEPROM_SIZE		0x0046
> +#define CONFIG_EEPROM_SLICES		5
> +
> +/* USER EEPROM is at addresses 0x8100 - 0x82FF */
> +#define USER_EEPROM_BASE		0x8100
> +#define USER_EEPROM_SIZE		0x0200
> +#define USER_EEPROM_SLICES		32
> +
> +/* MAX6875 commands */
> +#define MAX6875_CMD_BLOCK_WRITE		0x83
> +#define MAX6875_CMD_BLOCK_READ		0x84
> +#define MAX6875_CMD_REBOOT		0x88
> +
> +enum max6875_area_type {
> +	max6875_register_config=0,
> +	max6875_eeprom_config,
> +	max6875_eeprom_user,
> +
why empty line?

> +	max6857_max
> +};
> +
> +struct eeprom_block {
> +	enum max6875_area_type type;
> +	u8              slices;
> +	u32             size;
> +	u32             valid;
> +	u32             base;
> +	unsigned long   *updated;
> +	u8              *data;
> +};

I guess there should be tabs instead of spaces in whole struct

> +/* Each client has this additional data */
> +struct max6875_data {
> +	struct i2c_client       client;
> +
> +	struct semaphore        update_lock;
> +
> +	struct eeprom_block     blocks[max6857_max];
> +
Please use tabs

> +	/* the above structs point into the arrays below */
> +	u8 data[USER_EEPROM_SIZE + (CONFIG_EEPROM_SIZE*2)];
> +	unsigned long last_updated[USER_EEPROM_SLICES + (CONFIG_EEPROM_SLICES*2)];
> +};
> 
I'm not sure if I like the empty lines, but If you do you can leave them.

> +static int max6875_attach_adapter(struct i2c_adapter *adapter);
> +static int max6875_detect(struct i2c_adapter *adapter, int address, int kind);
> +static int max6875_detach_client(struct i2c_client *client);
> +
> +/* This is the driver that will be inserted */
> +static struct i2c_driver max6875_driver = {
> +	.owner          = THIS_MODULE,
> +	.name           = "max6875",
> +	.id             = I2C_DRIVERID_EEPROM,

I think ID was obsoleted some time ago, so you may to remove .id

> +	.flags          = I2C_DF_NOTIFY,
> +	.attach_adapter = max6875_attach_adapter,
> +	.detach_client  = max6875_detach_client,
> +};

Also in whole structure use tabs.

> +static void max6875_update_slice(struct i2c_client *client, 
> +				 struct eeprom_block *blk,
> +				 int slice)
> +{
> +	struct max6875_data *data = i2c_get_clientdata(client);
> +	int i, j, addr, count;
> +	u8 rdbuf[SLICE_SIZE];
> +
> +	if (slice >= blk->slices)
> +		return;
> +
> +	down(&data->update_lock);
> +
> +	if (!(blk->valid & (1 << slice)) ||
> +	    (jiffies - blk->updated[slice] > 300 * HZ) ||
> +	    (jiffies < blk->updated[slice])) {
> +		dev_dbg(&client->dev, "Starting eeprom update, slice %u, base %u\n", 
> +			slice, blk->base);
> +
> +		addr = blk->base + (slice << SLICE_BITS);
> +		count = blk->size - (slice << SLICE_BITS);
> +		if (count > SLICE_SIZE) {
> +			count = SLICE_SIZE;
> +		}
> +
> +		/* Preset the read address */
> +		if (addr < 0x100) {
> +			/* select the register */
> +			if (i2c_smbus_write_byte(client, addr & 0xFF)) {
> +				dev_dbg(&client->dev, "max6875 register select has failed!\n");
> +				goto exit;
> +			}
> +		} else {
> +			/* select the eeprom */
> +			if (i2c_smbus_write_byte_data(client, addr >> 8, addr & 0xFF)) {
> +				dev_dbg(&client->dev, "max6875 address set has failed!\n");
> +				goto exit;
> +			}
> +		}
> +
> +		if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
> +			if (i2c_smbus_read_i2c_block_data(client, MAX6875_CMD_BLOCK_READ, 
> +							  rdbuf) != SLICE_SIZE)
> +				goto exit;
> +
> +			memcpy(&blk->data[slice << SLICE_BITS], rdbuf, count);
> +		} else {
> +			for (i = 0; i < count; i++) {
> +				j = i2c_smbus_read_byte(client);
> +				if (j < 0)
> +					goto exit;
> +				blk->data[(slice << SLICE_BITS) + i] = (u8) j;
> +			}
> +		}
> +		blk->updated[slice] = jiffies;
> +		blk->valid |= (1 << slice);
> +	}
> +	exit:
> +	up(&data->update_lock);
> +}
> +
> +static ssize_t max6875_read(struct kobject *kobj, char *buf, loff_t off, size_t count, 
> +			    enum max6875_area_type area_type)
> +{
> +	struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
> +	struct max6875_data *data = i2c_get_clientdata(client);
> +	struct eeprom_block *blk;
> +	int slice;
> +
> +	blk = &data->blocks[area_type];
> +
> +	if (off > blk->size)
> +		return 0;
> +	if (off + count > blk->size)
> +		count = blk->size - off;
> +
> +	/* Only refresh slices which contain requested bytes */
> +	for (slice = (off >> SLICE_BITS); slice <= ((off + count - 1) >> SLICE_BITS); slice++)
> +		max6875_update_slice(client, blk, slice);
> +
> +	memcpy(buf, &blk->data[off], count);
> +
> +	return count;
> +}
> +
> +static ssize_t max6875_user_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
> +{
> +	return max6875_read(kobj, buf, off, count, max6875_eeprom_user);
> +}
> +
> +static ssize_t max6875_config_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
> +{
> +	return max6875_read(kobj, buf, off, count, max6875_eeprom_config);
> +}
> +
> +static ssize_t max6875_cfgreg_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
> +{
> +	return max6875_read(kobj, buf, off, count, max6875_register_config);
> +}
> +
> +
> +static ssize_t max6875_write(struct kobject *kobj, char *buf, loff_t off, size_t count, 
> +			     enum max6875_area_type area_type)
> +{
> +	struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
> +	struct max6875_data *data = i2c_get_clientdata(client);
> +	struct eeprom_block *blk;
> +	int slice, addr, retval;
> +	ssize_t sent = 0;
> +
> +	blk = &data->blocks[area_type];
> +
> +	printk(KERN_WARNING "max6875_write: off:%d count:%d type:%d blksize:%d\n",
> +	       (int)off, count, area_type, blk->size);

Kernel has dev_warn for this

> +
> +	if (off > blk->size)
> +		return 0;
> +	if ((off + count) > blk->size)
> +		count = blk->size - off;
> +
> +	if (down_interruptible(&data->update_lock))
> +		return -EAGAIN;
> +
> +	/* writing to a register is done with i2c_smbus_write_byte_data() */
> +	if (blk->type == max6875_register_config) {
> +		for (sent = 0; sent < count; sent++) {
> +			addr = off + sent;
> +			if (addr == 0x44)
> +				continue;
> +
> +			retval = i2c_smbus_write_byte_data(client, addr, buf[sent]);
> +			printk("reg: write_byte_data(0x%X,0x%X) => %d\n",
> +			       addr, buf[sent], retval);
Kernel has dev_warn for this
> +		}
> +	} else {
> +		int cmd, val;
> +
> +		/* We are writing to EEPROM */
> +		for (sent = 0; sent < count; sent++) {
> +			addr = blk->base + off + sent;
> +			cmd = addr >> 8;
> +			val = (addr & 0xff) | (buf[sent] << 8);	// reversed
> +
> +			if (addr == 0x8044)
> +				continue;

Btw what about:

For the configuration EEPROM, valid address pointers
range from 8000h to 8045h. Registers 8046h to 804Fh
are reserved and should not be overwritten. Register

?

> +
> +			retval = i2c_smbus_write_word_data(client, cmd, val);
> +
> +			if (retval) {
> +				goto error_exit;
> +			}
> +
> +			/* A write takes up to 11 ms */
> +			msleep(11);
> +		}
> +	}
> +
> +	/* Invalidate the scratch buffer */
> +	for (slice = (off >> SLICE_BITS); slice <= ((off + count - 1) >> SLICE_BITS); slice++)
> +		blk->valid &= ~(1 << slice);
> +
> +	error_exit:
> +	up(&data->update_lock);
> +
> +	return sent;
> +}
> +
> +static ssize_t max6875_user_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
> +{
> +	return max6875_write(kobj, buf, off, count, max6875_eeprom_user);
> +}
> +
> +static ssize_t max6875_config_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
> +{
> +	return max6875_write(kobj, buf, off, count, max6875_eeprom_config);
> +}
> +
> +static ssize_t max6875_cfgreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
> +{
> +	return max6875_write(kobj, buf, off, count, max6875_register_config);
> +}
> +
> +static struct bin_attribute user_eeprom_attr = {
> +	.attr = {
> +		.name = "eeprom_user",
> +		.mode = S_IRUGO | S_IWUSR | S_IWGRP,
> +		.owner = THIS_MODULE,
> +	},
> +	.size  = USER_EEPROM_SIZE,
> +	.read  = max6875_user_read,
> +	.write = max6875_user_write,
> +};
> +
> +static struct bin_attribute config_eeprom_attr = {
> +	.attr = {
> +		.name = "eeprom_config",
> +		.mode = S_IRUGO | S_IWUSR,
> +		.owner = THIS_MODULE,
> +	},
> +	.size  = CONFIG_EEPROM_SIZE,
> +	.read  = max6875_config_read,
> +	.write = max6875_config_write,
> +};
> +
> +static struct bin_attribute config_register_attr = {
> +	.attr = {
> +		.name = "reg_config",
> +		.mode = S_IRUGO | S_IWUSR,
> +		.owner = THIS_MODULE,
> +	},
> +	.size  = CONFIG_EEPROM_SIZE,
> +	.read  = max6875_cfgreg_read,
> +	.write = max6875_cfgreg_write,
> +};
> +
> +static int max6875_attach_adapter(struct i2c_adapter *adapter)
> +{
> +	return i2c_detect(adapter, &addr_data, max6875_detect);
> +}
> +
> +/* This function is called by i2c_detect */
> +static int max6875_detect(struct i2c_adapter *adapter, int address, int kind)
> +{
> +	struct i2c_client *new_client;
> +	struct max6875_data *data;
> +	int err = 0;
> +
> +	/* TODO: is there any way to actually DETECT this chip? */

Some addresses are always zero. And some registers too.

> +
> +	/* There are three ways we can read the EEPROM data:
> +	   (1) I2C block reads (faster, but unsupported by most adapters)
> +	   (2) Consecutive byte reads (100% overhead)
> +	   (3) Regular byte data reads (200% overhead)
> +	   The third method is not implemented by this driver because all
> +	   known adapters support at least the second. */
> +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA | 
> +				     I2C_FUNC_SMBUS_BYTE |
> +				     I2C_FUNC_SMBUS_WRITE_BYTE_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 eeprom_{read,write}_value. */
> +	if (!(data = kmalloc(sizeof(struct max6875_data), GFP_KERNEL))) {
> +		err = -ENOMEM;
> +		goto exit;
> +	}
> +	memset(data, 0, sizeof(struct max6875_data));
> +
> +	new_client = &data->client;
> +	i2c_set_clientdata(new_client, data);
> +	new_client->addr = address;
> +	new_client->adapter = adapter;
> +	new_client->driver = &max6875_driver;
> +	new_client->flags = 0;
> +
> +	/* Setup the user section */
> +	data->blocks[max6875_eeprom_user].type    = max6875_eeprom_user;
> +	data->blocks[max6875_eeprom_user].slices  = USER_EEPROM_SLICES;
> +	data->blocks[max6875_eeprom_user].size    = USER_EEPROM_SIZE;
> +	data->blocks[max6875_eeprom_user].base    = USER_EEPROM_BASE;
> +	data->blocks[max6875_eeprom_user].data    = data->data;
> +	data->blocks[max6875_eeprom_user].updated = data->last_updated;
> +
> +	/* Setup the config section */
> +	data->blocks[max6875_eeprom_config].type    = max6875_eeprom_config;
> +	data->blocks[max6875_eeprom_config].slices  = CONFIG_EEPROM_SLICES;
> +	data->blocks[max6875_eeprom_config].size    = CONFIG_EEPROM_SIZE;
> +	data->blocks[max6875_eeprom_config].base    = CONFIG_EEPROM_BASE;
> +	data->blocks[max6875_eeprom_config].data    = &data->data[USER_EEPROM_SIZE];
> +	data->blocks[max6875_eeprom_config].updated = &data->last_updated[USER_EEPROM_SLICES];
> +
> +	/* Setup the register section */
> +	data->blocks[max6875_register_config].type    = max6875_register_config;
> +	data->blocks[max6875_register_config].slices  = CONFIG_EEPROM_SLICES;
> +	data->blocks[max6875_register_config].size    = CONFIG_EEPROM_SIZE;
> +	data->blocks[max6875_register_config].base    = 0;
> +	data->blocks[max6875_register_config].data    = &data->data[USER_EEPROM_SIZE+CONFIG_EEPROM_SIZE];
> +	data->blocks[max6875_register_config].updated = &data->last_updated[USER_EEPROM_SLICES+CONFIG_EEPROM_SLICES];
> +
> +	/* Init the data */
> +	memset(data->data, 0xff, sizeof(data->data));
> +
> +	/* Fill in the remaining client fields */
> +	strlcpy(new_client->name, "max6875", I2C_NAME_SIZE);
> +	init_MUTEX(&data->update_lock);
> +
> +	/* Tell the I2C layer a new client has arrived */
> +	if ((err = i2c_attach_client(new_client)))
> +		goto exit_kfree;
> +
> +	/* create the sysfs eeprom files with the correct permissions */
> +	if (allow_write == 0) {
> +		user_eeprom_attr.attr.mode &= ~S_IWUGO;
> +		user_eeprom_attr.write = NULL;
> +		config_eeprom_attr.attr.mode &= ~S_IWUGO;
> +		config_eeprom_attr.write = NULL;
> +		config_register_attr.attr.mode &= ~S_IWUGO;
> +		config_register_attr.write = NULL;
> +	}
> +	sysfs_create_bin_file(&new_client->dev.kobj, &user_eeprom_attr);
> +	sysfs_create_bin_file(&new_client->dev.kobj, &config_eeprom_attr);
> +	sysfs_create_bin_file(&new_client->dev.kobj, &config_register_attr);
> +
> +	return 0;
> +
> +	exit_kfree:
> +	kfree(data);
> +	exit:
> +	return err;
> +}
> +
> +static int max6875_detach_client(struct i2c_client *client)
> +{
> +	int err;
> +
> +	err = i2c_detach_client(client);
> +	if (err) {
> +		dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
> +		return err;
> +	}
> +
> +	kfree(i2c_get_clientdata(client));
> +
> +	return 0;
> +}
> +
> +static int __init max6875_init(void)
> +{
> +	return i2c_add_driver(&max6875_driver);
> +}
> +
> +static void __exit max6875_exit(void)
> +{
> +	i2c_del_driver(&max6875_driver);
> +}
> +
> +
> +MODULE_AUTHOR("Ben Gardner <bgardner at wabtec.com>");
> +MODULE_DESCRIPTION("MAX6875 driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(max6875_init);
> +module_exit(max6875_exit);





[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux