All of lore.kernel.org
 help / color / mirror / Atom feed
From: waltergoossens@home.nl (Walter Goossens)
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] RFC patch to add eeprom write support
Date: Mon, 27 Nov 2006 23:04:29 +0000	[thread overview]
Message-ID: <456B6EFD.6090302@home.nl> (raw)
In-Reply-To: <16475947.1164204132228.JavaMail.root@webmail2.tilbu1>

Fixed documentation thanks to David Hubbard

======

Added support for the MAX7311 i2c io port expanders. This driver is
based on the pcf8574 driver which supports the same kind of chip.

Signed off by Walter Goossens <waltergoossens at home.nl>

diff -uarN -x '*.o' -x '*.ko' linux-2.6.17/drivers/i2c/chips/Kconfig 
linux-2.6.17-mpe04/drivers/i2c/chips/Kconfig
--- linux-2.6.17/drivers/i2c/chips/Kconfig    2006-06-18 
01:49:35.000000000 +0000
+++ linux-2.6.17-mpe04/drivers/i2c/chips/Kconfig    2006-10-18 
11:00:05.000000000 +0000
@@ -117,4 +117,23 @@
       This driver can also be built as a module.  If so, the module
       will be called max6875.
 
+config SENSORS_MAX7311
+    tristate "Maxim MAX7311 16 port io expander"
+    depends on I2C
+    help
+      If you say yes here you will get support for the max7311 port
+      expander chip. This chip has 16 ioports each configurable for
+      input or output.
+     
+      This driver can also be built as a module. If so, the module
+      will be called max7311.
+config SENSORS_MAX7311_NUMCLIENTS
+    int "Maximum number of chips"
+    default "4"
+    depends on SENSORS_MAX7311
+config SENSORS_MAX7311_ADDRESSES
+    string "Addresses to scan"
+    default "0x20 0x24"
+    depends on SENSORS_MAX7311
+
 endmenu
diff -uarN -x '*.o' -x '*.ko' linux-2.6.17/drivers/i2c/chips/Makefile 
linux-2.6.17-mpe04/drivers/i2c/chips/Makefile
--- linux-2.6.17/drivers/i2c/chips/Makefile    2006-06-18 
01:49:35.000000000 +0000
+++ linux-2.6.17-mpe04/drivers/i2c/chips/Makefile    2006-10-18 
10:00:29.000000000 +0000
@@ -6,6 +6,7 @@
 obj-$(CONFIG_SENSORS_DS1374)    += ds1374.o
 obj-$(CONFIG_SENSORS_EEPROM)    += eeprom.o
 obj-$(CONFIG_SENSORS_MAX6875)    += max6875.o
+obj-$(CONFIG_SENSORS_MAX7311)    += max7311.o
 obj-$(CONFIG_SENSORS_M41T00)    += m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)    += pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)    += pcf8574.o
diff -uarN -x '*.o' -x '*.ko' linux-2.6.17/drivers/i2c/chips/max7311.c 
linux-2.6.17-mpe04/drivers/i2c/chips/max7311.c
--- linux-2.6.17/drivers/i2c/chips/max7311.c    1970-01-01 
00:00:00.000000000 +0000
+++ linux-2.6.17-mpe04/drivers/i2c/chips/max7311.c    2006-10-18 
11:27:59.000000000 +0000
@@ -0,0 +1,352 @@
+/*
+    max7311.c
+    Copyright (c) 2006  Walter Goossens <walter.goossens at axon.tv>
+   
+    Based on:
+   
+    pcf8574.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol at dds.nl>,
+                        Philip Edelbrock <phil at netroedge.com>,
+                        Dan Eaton <dan.eaton at rocketlogix.com>
+    Ported to Linux 2.6 by Aurelien Jarno <aurel32 at debian.org> with
+    the help of Jean Delvare <khali at 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/list.h>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[CONFIG_SENSORS_MAX7311_NUMCLIENTS];
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
+
+/* Initial values */
+#define MAX7311_INIT 0xFFFF  /* All ports in input */
+/* Chip registers */
+#define REG_INPUT       0x00
+#define REG_OUTPUT      0x02
+#define REG_POLARITY    0x04
+#define REG_CONFIG      0x06
+#define REG_TIMEOUT     0x08
+
+/* Keep a list of all registered clients */
+static LIST_HEAD(max7311_clients);
+
+/* Each client has this additional data */
+struct max7311_data {
+  struct i2c_client client;
+
+  uint16_t output;
+  uint16_t config;
+  uint16_t polarity;
+  struct list_head list;
+};
+
+
+static int max7311_attach_adapter(struct i2c_adapter *adapter);
+static int max7311_detect(struct i2c_adapter *adapter, int address, int 
kind);
+static int max7311_detach_client(struct i2c_client *client);
+static void max7311_init_client(struct i2c_client *client);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max7311_driver = {
+  .driver = {
+    .name = "max7311",
+  },
+  .id   = I2C_DRIVERID_MAX7311,
+  .attach_adapter = max7311_attach_adapter,
+  .detach_client  = max7311_detach_client,
+};
+
+/* following are the sysfs callback functions */
+static ssize_t show_read(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+  struct i2c_client *client = to_i2c_client(dev);
+  return sprintf(buf, "%u\n", i2c_smbus_read_word_data(client,REG_INPUT));
+}
+
+static DEVICE_ATTR(read, S_IRUGO, show_read, NULL);
+
+static ssize_t show_write(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+  struct max7311_data *data = i2c_get_clientdata(to_i2c_client(dev));
+  return sprintf(buf, "%u\n", data->output);
+}
+
+static ssize_t set_write(struct device *dev, struct device_attribute 
*attr, const char *buf, size_t count)
+{
+  struct i2c_client *client = to_i2c_client(dev);
+  struct max7311_data *data = i2c_get_clientdata(client);
+  unsigned long val = simple_strtoul(buf, NULL, 10);
+
+  if (val > 0xFFFF)
+  {
+    return -EINVAL;
+  }
+  data->output = val;
+  i2c_smbus_write_word_data(client, REG_OUTPUT,data->output);
+  return count;
+}
+
+static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
+
+static ssize_t show_config(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+  struct max7311_data *data = i2c_get_clientdata(to_i2c_client(dev));
+  return sprintf(buf, "%u\n", data->config);
+}
+
+static ssize_t set_config(struct device *dev, struct device_attribute 
*attr, const char *buf, size_t count)
+{
+  struct i2c_client *client = to_i2c_client(dev);
+  struct max7311_data *data = i2c_get_clientdata(client);
+  unsigned long val = simple_strtoul(buf, NULL, 10);
+
+  if (val > 0xFFFF)
+  {
+    return -EINVAL;
+  }
+  data->config = val;
+  i2c_smbus_write_word_data(client, REG_CONFIG,data->config);
+  return count;
+}
+
+static DEVICE_ATTR(config, S_IWUSR | S_IRUGO, show_config, set_config);
+
+static ssize_t show_polarity(struct device *dev, struct 
device_attribute *attr, char *buf)
+{
+  struct max7311_data *data = i2c_get_clientdata(to_i2c_client(dev));
+  return sprintf(buf, "%u\n", data->polarity);
+}
+
+static ssize_t set_polarity(struct device *dev, struct device_attribute 
*attr, const char *buf, size_t count)
+{
+  struct i2c_client *client = to_i2c_client(dev);
+  struct max7311_data *data = i2c_get_clientdata(client);
+  unsigned long val = simple_strtoul(buf, NULL, 10);
+
+  if (val > 0xFFFF)
+  {
+    return -EINVAL;
+  }
+  data->polarity = val;
+  i2c_smbus_write_word_data(client, REG_CONFIG,data->polarity);
+  return count;
+}
+
+static DEVICE_ATTR(polarity, S_IWUSR | S_IRUGO, show_polarity, 
set_polarity);
+
+/*
+ * Real code
+ */
+
+static int max7311_attach_adapter(struct i2c_adapter *adapter)
+{
+  return i2c_probe(adapter, &addr_data, max7311_detect);
+}
+
+/* This function is called by i2c_probe */
+static int max7311_detect(struct i2c_adapter *adapter, int address, int 
kind)
+{
+  struct i2c_client *new_client;
+  struct max7311_data *data;
+  int err = 0;
+  /* Can out adapter suply everything we need ? */
+  if (!i2c_check_functionality(adapter, 
I2C_FUNC_SMBUS_BYTE|I2C_FUNC_SMBUS_BYTE_DATA))
+  {
+    return -1;
+  }
+
+  /* OK. For now, we presume we have a valid client. We now create the
+  client structure, even though we cannot fill it completely yet. */
+  if (!(data = kzalloc(sizeof(struct max7311_data), GFP_KERNEL))) {
+    err = -ENOMEM;
+    return err;
+  }
+
+  new_client = &data->client;
+  i2c_set_clientdata(new_client, data);
+  new_client->addr = address;
+  new_client->adapter = adapter;
+  new_client->driver = &max7311_driver;
+  new_client->flags = 0;
+
+  /* Fill in the remaining client fields and put it into the global list */
+  strlcpy(new_client->name, "max7311", I2C_NAME_SIZE);
+
+  /* Tell the I2C layer a new client has arrived */
+  if ((err = i2c_attach_client(new_client)))
+  {
+    kfree(data);
+    return err;
+  }
+  /* Initialize the PCF8574 chip */
+  max7311_init_client(new_client);
+
+  /* Register sysfs hooks */
+  device_create_file(&new_client->dev, &dev_attr_read);
+  device_create_file(&new_client->dev, &dev_attr_write);
+  device_create_file(&new_client->dev, &dev_attr_config);
+  device_create_file(&new_client->dev, &dev_attr_polarity);
+  return 0;
+}
+
+static int max7311_detach_client(struct i2c_client *client)
+{
+  int err;
+  struct max7311_data *data = i2c_get_clientdata(client);
+ 
+  if ((err = i2c_detach_client(client)))
+  {
+    return err;
+  }
+  list_del(&data->list);
+  kfree(data);
+  return 0;
+}
+
+/* Called when we have found a new MAX7311. */
+static void max7311_init_client(struct i2c_client *client)
+{
+  struct max7311_data *data = i2c_get_clientdata(client);
+  //Default all ports to inputs!
+  data->config = i2c_smbus_read_word_data(client,REG_CONFIG);
+  data->output = i2c_smbus_read_word_data(client,REG_OUTPUT);
+  data->polarity = i2c_smbus_read_word_data(client,REG_POLARITY);
+  //Enable bus timeout
+  i2c_smbus_write_byte_data(client,REG_TIMEOUT,0x01);
+  //Register with our list
+  INIT_LIST_HEAD(&data->list);
+  list_add_tail(&data->list, &max7311_clients);
+}
+
+struct max7311_data * get_max7311_client(int bus, int address)
+{
+  struct list_head *walk;
+  struct list_head *tmp;
+  struct max7311_data *data;
+
+  list_for_each_safe(walk, tmp, &max7311_clients) {
+    data = list_entry(walk, struct max7311_data, list);
+    if ((data->client.adapter->nr = bus)&&(data->client.addr=address))
+      return data;
+  }
+  return NULL;
+}
+
+static int do_max7311_write(int bus, int address, int chipReg, int newData)
+{
+  struct max7311_data *data;
+  if((data=get_max7311_client(bus,address)))
+  {
+    switch(chipReg) {
+      case REG_OUTPUT:
+      {
+        data->output = newData;
+        return i2c_smbus_write_word_data(&data->client,REG_OUTPUT,newData);
+      } break;
+      case REG_POLARITY:
+      {
+        data->polarity = newData;
+        return 
i2c_smbus_write_word_data(&data->client,REG_POLARITY,newData);
+      } break;
+      case REG_CONFIG:
+      {
+        data->config = newData;
+        return i2c_smbus_write_word_data(&data->client,REG_CONFIG,newData);
+      }
+      default:
+      {
+        return -1;
+      }
+    }
+  } else {
+    return -ENODEV;
+  }
+}
+static int do_max7311_read(int bus, int address, int chipReg)
+{
+  struct max7311_data *data;
+  if((data=get_max7311_client(bus,address)))
+  {
+    switch(chipReg) {
+      case REG_INPUT:
+      {
+        return i2c_smbus_read_word_data(&data->client,REG_INPUT);
+      } break;
+      case REG_OUTPUT:
+      {
+        return data->output;
+      } break;
+      case REG_POLARITY:
+      {
+        return data->polarity;
+      } break;
+      case REG_CONFIG:
+      {
+        return data->config;
+      }
+      default:
+      {
+        return -1;
+      }
+    }
+  } else {
+    return -ENODEV;
+  }
+}
+
+static int __init max7311_init(void)
+{
+  int i;
+  unsigned short addr;
+  char *all_addr = CONFIG_SENSORS_MAX7311_ADDRESSES;
+  for(i=0; i<CONFIG_SENSORS_MAX7311_NUMCLIENTS; i++) {
+    addr = simple_strtoul(all_addr,&all_addr,16);
+    if(all_addr[0]!='\0') {
+      all_addr++;
+    }
+    if(addr)
+    {
+      normal_i2c[i] = addr;
+    } else {
+      normal_i2c[i] = I2C_CLIENT_END;
+    }
+  }
+  return i2c_add_driver(&max7311_driver);
+}
+
+static void __exit max7311_exit(void)
+{
+  i2c_del_driver(&max7311_driver);
+}
+
+
+MODULE_AUTHOR("Walter Goossens <walter.goossens at axon.tv>");
+MODULE_DESCRIPTION("MAX7311 driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(do_max7311_read);
+EXPORT_SYMBOL(do_max7311_write);
+
+module_init(max7311_init);
+module_exit(max7311_exit);



      parent reply	other threads:[~2006-11-27 23:04 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-11-22 14:02 [lm-sensors] RFC patch to add eeprom write support waltergoossens at home.nl
2006-11-22 17:48 ` David Hubbard
2006-11-22 22:45 ` Walter Goossens
2006-11-27 23:04 ` Walter Goossens [this message]

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=456B6EFD.6090302@home.nl \
    --to=waltergoossens@home.nl \
    --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.