I back-ported the AD7418 temp/voltage sensor driver from 2.6.22
to OpenWrt 7.07. Complete patch attached, and also copies of
the various post-patched files for ease of reviewing.

I didn't totally understand what I was doing with the build file
changes in openwrt so please excuse any blunders. I copied
existing macros. Note that ad7418 depends on hwmon, however
I do not explicitly declare that dependency in openwrt because
there is no package for hwmon (there is a package for hwmon-vid,
but that driver doesn't pertain to ixp4xx hardware and the kernel
build doesn't make it even if you ask it to in that case so...
I guess I'm depending on the kernel config enabling hwmon.
It appears that it does so by default and that hwmon is always built-in).

I used the newest code from 2.6.22, and not one of the older patches
that I found around the 'net.

This driver is useful to anyone with Gateworks Avila hardware,
as it allows temperature and board power supply input voltage
monitoring.

If you have 7.07 installed on a Gateworks Avila or other
board with this device, you should be able to apply my patches
to a checked-out 7.07 tree, run make menuconfig and under
kernel modules->other modules you should enable kmod-hwmon-ad7418
Then build which should produce this package:
bin/packages/kmod-hwmon-ad7418_2.6.21.6-ixp4xx-1_armeb.ipk

Install that package on the board then you should see this:

[EMAIL PROTECTED]:~# ipkg install kmod-hwmon-ad7418_2.6.21.6-ixp4xx-1_armeb.ipk
Installing kmod-hwmon-ad7418 (2.6.21.6-ixp4xx-1) to root...
Configuring kmod-hwmon-ad7418
Done.
[EMAIL PROTECTED]:~# lsmod
Module                  Size  Used by    Tainted: P
ad7418                  3672  0
...

[EMAIL PROTECTED]:~# dmesg
...
ad7418 0-0028: ad7418 chip found
ad7418 0-0028: configuring for mode 1

And now you can read temp and voltage like this :

[EMAIL PROTECTED]:~# cat /sys/bus/i2c/devices/0-0028/temp1_input
45500
[EMAIL PROTECTED]:~# cat /sys/bus/i2c/devices/0-0028/in1_input
669

45500 above means 45.5 degrees C (the chip has 0.25C resolution
but the driver quantizes on 0.5C due to the use of a shared macro
from lm_sensors).

669 above means (669/1024) * 2.5v read at the input to the chip.
The Avila has a divider between the board input and the chip with
a 23:1 ratio (actually on my board I measure it as 27:1).

Any comments/complaints/feedback welcome.
If this is acceptable I'd also like to figure out how to get it
into svn.

Thanks.







Index: target/linux/ixp4xx-2.6/config/default
===================================================================
--- target/linux/ixp4xx-2.6/config/default      (revision 8834)
+++ target/linux/ixp4xx-2.6/config/default      (working copy)
@@ -407,6 +407,7 @@
 # CONFIG_SENSORS_ADM1026 is not set
 # CONFIG_SENSORS_ADM1029 is not set
 # CONFIG_SENSORS_ADM1031 is not set
+CONFIG_SENSORS_AD7418=m
 # CONFIG_SENSORS_ADM9240 is not set
 # CONFIG_SENSORS_ASB100 is not set
 # CONFIG_SENSORS_ATXP1 is not set
Index: target/linux/ixp4xx-2.6/patches/910-ad7418.patch
===================================================================
--- target/linux/ixp4xx-2.6/patches/910-ad7418.patch    (revision 0)
+++ target/linux/ixp4xx-2.6/patches/910-ad7418.patch    (revision 0)
@@ -0,0 +1,408 @@
+diff -Naur linux-2.6.21.6/drivers/hwmon/ad7418.c 
linux-2.6.21.6_patched/drivers/hwmon/ad7418.c
+--- linux-2.6.21.6/drivers/hwmon/ad7418.c      1969-12-31 17:00:00.000000000 
-0700
++++ linux-2.6.21.6_patched/drivers/hwmon/ad7418.c      2007-09-17 
22:03:43.000000000 -0600
+@@ -0,0 +1,373 @@
++/*
++ * An hwmon driver for the Analog Devices AD7416/17/18
++ * Copyright (C) 2006-07 Tower Technologies
++ *
++ * Author: Alessandro Zummo <[EMAIL PROTECTED]>
++ *
++ * Based on lm75.c
++ * Copyright (C) 1998-99 Frodo Looijaard <[EMAIL PROTECTED]>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License,
++ * as published by the Free Software Foundation - version 2.
++ */
++
++#include <linux/module.h>
++#include <linux/jiffies.h>
++#include <linux/i2c.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/err.h>
++#include <linux/mutex.h>
++#include <linux/delay.h>
++
++#include "lm75.h"
++
++#define DRV_VERSION "0.3"
++
++/* Addresses to scan */
++static unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END };
++/* Insmod parameters */
++I2C_CLIENT_INSMOD_3(ad7416, ad7417, ad7418);
++
++/* AD7418 registers */
++#define AD7418_REG_TEMP_IN    0x00
++#define AD7418_REG_CONF               0x01
++#define AD7418_REG_TEMP_HYST  0x02
++#define AD7418_REG_TEMP_OS    0x03
++#define AD7418_REG_ADC                0x04
++#define AD7418_REG_CONF2      0x05
++
++#define AD7418_REG_ADC_CH(x)  ((x) << 5)
++#define AD7418_CH_TEMP                AD7418_REG_ADC_CH(0)
++
++static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
++                                      AD7418_REG_TEMP_HYST,
++                                      AD7418_REG_TEMP_OS };
++
++struct ad7418_data {
++      struct i2c_client       client;
++      struct class_device     *class_dev;
++      struct attribute_group  attrs;
++      enum chips              type;
++      struct mutex            lock;
++      int                     adc_max;        /* number of ADC channels */
++      char                    valid;
++      unsigned long           last_updated;   /* In jiffies */
++      s16                     temp[3];        /* Register values */
++      u16                     in[4];
++};
++
++static int ad7418_attach_adapter(struct i2c_adapter *adapter);
++static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind);
++static int ad7418_detach_client(struct i2c_client *client);
++
++static struct i2c_driver ad7418_driver = {
++      .driver = {
++              .name   = "ad7418",
++      },
++      .attach_adapter = ad7418_attach_adapter,
++      .detach_client  = ad7418_detach_client,
++};
++
++/* All registers are word-sized, except for the configuration registers.
++ * AD7418 uses a high-byte first convention. Do NOT use those functions to
++ * access the configuration registers CONF and CONF2, as they are byte-sized.
++ */
++static inline int ad7418_read(struct i2c_client *client, u8 reg)
++{
++      return swab16(i2c_smbus_read_word_data(client, reg));
++}
++
++static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value)
++{
++      return i2c_smbus_write_word_data(client, reg, swab16(value));
++}
++
++static void ad7418_init_client(struct i2c_client *client)
++{
++      struct ad7418_data *data = i2c_get_clientdata(client);
++
++      int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
++      if (reg < 0) {
++              dev_err(&client->dev, "cannot read configuration register\n");
++      } else {
++              dev_info(&client->dev, "configuring for mode 1\n");
++              i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe);
++
++              if (data->type == ad7417 || data->type == ad7418)
++                      i2c_smbus_write_byte_data(client,
++                                              AD7418_REG_CONF2, 0x00);
++      }
++}
++
++static struct ad7418_data *ad7418_update_device(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct ad7418_data *data = i2c_get_clientdata(client);
++
++      mutex_lock(&data->lock);
++
++      if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
++              || !data->valid) {
++              u8 cfg;
++              int i, ch;
++
++              /* read config register and clear channel bits */
++              cfg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
++              cfg &= 0x1F;
++
++              i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
++                                              cfg | AD7418_CH_TEMP);
++              udelay(30);
++
++              for (i = 0; i < 3; i++) {
++                      data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]);
++              }
++
++              for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
++                      i2c_smbus_write_byte_data(client,
++                                      AD7418_REG_CONF,
++                                      cfg | AD7418_REG_ADC_CH(ch));
++
++                      udelay(15);
++                      data->in[data->adc_max - 1 - i] =
++                              ad7418_read(client, AD7418_REG_ADC);
++              }
++
++              /* restore old configuration value */
++              ad7418_write(client, AD7418_REG_CONF, cfg);
++
++              data->last_updated = jiffies;
++              data->valid = 1;
++      }
++
++      mutex_unlock(&data->lock);
++
++      return data;
++}
++
++static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
++                      char *buf)
++{
++      struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++      struct ad7418_data *data = ad7418_update_device(dev);
++      return sprintf(buf, "%d\n",
++              LM75_TEMP_FROM_REG(data->temp[attr->index]));
++}
++
++static ssize_t show_adc(struct device *dev, struct device_attribute *devattr,
++                      char *buf)
++{
++      struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++      struct ad7418_data *data = ad7418_update_device(dev);
++
++      return sprintf(buf, "%d\n",
++              ((data->in[attr->index] >> 6) * 2500 + 512) / 1024);
++}
++
++static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
++                      const char *buf, size_t count)
++{
++      struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++      struct i2c_client *client = to_i2c_client(dev);
++      struct ad7418_data *data = i2c_get_clientdata(client);
++      int temp = simple_strtol(buf, NULL, 10);
++
++      mutex_lock(&data->lock);
++      data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
++      ad7418_write(client, AD7418_REG_TEMP[attr->index], 
data->temp[attr->index]);
++      mutex_unlock(&data->lock);
++      return count;
++}
++
++static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
++static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
++                              show_temp, set_temp, 1);
++static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
++                              show_temp, set_temp, 2);
++
++static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0);
++static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1);
++static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2);
++static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3);
++
++static int ad7418_attach_adapter(struct i2c_adapter *adapter)
++{
++      if (!(adapter->class & I2C_CLASS_HWMON))
++              return 0;
++      return i2c_probe(adapter, &addr_data, ad7418_detect);
++}
++
++static struct attribute *ad7416_attributes[] = {
++      &sensor_dev_attr_temp1_max.dev_attr.attr,
++      &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
++      &sensor_dev_attr_temp1_input.dev_attr.attr,
++      NULL
++};
++
++static struct attribute *ad7417_attributes[] = {
++      &sensor_dev_attr_temp1_max.dev_attr.attr,
++      &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
++      &sensor_dev_attr_temp1_input.dev_attr.attr,
++      &sensor_dev_attr_in1_input.dev_attr.attr,
++      &sensor_dev_attr_in2_input.dev_attr.attr,
++      &sensor_dev_attr_in3_input.dev_attr.attr,
++      &sensor_dev_attr_in4_input.dev_attr.attr,
++      NULL
++};
++
++static struct attribute *ad7418_attributes[] = {
++      &sensor_dev_attr_temp1_max.dev_attr.attr,
++      &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
++      &sensor_dev_attr_temp1_input.dev_attr.attr,
++      &sensor_dev_attr_in1_input.dev_attr.attr,
++      NULL
++};
++
++static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++      struct i2c_client *client;
++      struct ad7418_data *data;
++      int err = 0;
++
++      if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
++                                      I2C_FUNC_SMBUS_WORD_DATA))
++              goto exit;
++
++      if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) {
++              err = -ENOMEM;
++              goto exit;
++      }
++
++      client = &data->client;
++      client->addr = address;
++      client->adapter = adapter;
++      client->driver = &ad7418_driver;
++
++      i2c_set_clientdata(client, data);
++
++      mutex_init(&data->lock);
++
++      /* AD7418 has a curious behaviour on registers 6 and 7. They
++       * both always read 0xC071 and are not documented on the datasheet.
++       * We use them to detect the chip.
++       */
++      if (kind <= 0) {
++              int reg, reg6, reg7;
++
++              /* the AD7416 lies within this address range, but I have
++               * no means to check.
++               */
++              if (address >= 0x48 && address <= 0x4f) {
++                      /* XXX add tests for AD7416 here */
++                      /* data->type = ad7416; */
++              }
++              /* here we might have AD7417 or AD7418 */
++              else if (address >= 0x28 && address <= 0x2f) {
++                      reg6 = i2c_smbus_read_word_data(client, 0x06);
++                      reg7 = i2c_smbus_read_word_data(client, 0x07);
++
++                      if (address == 0x28 && reg6 == 0xC071 && reg7 == 0xC071)
++                              data->type = ad7418;
++
++                      /* XXX add tests for AD7417 here */
++
++
++                      /* both AD7417 and AD7418 have bits 0-5 of
++                       * the CONF2 register at 0
++                       */
++                      reg = i2c_smbus_read_byte_data(client,
++                                                      AD7418_REG_CONF2);
++                      if (reg & 0x3F)
++                              data->type = any_chip; /* detection failed */
++              }
++      } else {
++              dev_dbg(&adapter->dev, "detection forced\n");
++      }
++
++      if (kind > 0)
++              data->type = kind;
++      else if (kind < 0 && data->type == any_chip) {
++              err = -ENODEV;
++              goto exit_free;
++      }
++
++      switch (data->type) {
++      case any_chip:
++      case ad7416:
++              data->adc_max = 0;
++              data->attrs.attrs = ad7416_attributes;
++              strlcpy(client->name, "ad7416", I2C_NAME_SIZE);
++              break;
++
++      case ad7417:
++              data->adc_max = 4;
++              data->attrs.attrs = ad7417_attributes;
++              strlcpy(client->name, "ad7417", I2C_NAME_SIZE);
++              break;
++
++      case ad7418:
++              data->adc_max = 1;
++              data->attrs.attrs = ad7418_attributes;
++              strlcpy(client->name, "ad7418", I2C_NAME_SIZE);
++              break;
++      }
++
++      if ((err = i2c_attach_client(client)))
++              goto exit_free;
++
++      dev_info(&client->dev, "%s chip found\n", client->name);
++
++      /* Initialize the AD7418 chip */
++      ad7418_init_client(client);
++
++      /* Register sysfs hooks */
++      if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
++              goto exit_detach;
++
++      data->class_dev = hwmon_device_register(&client->dev);
++      if (IS_ERR(data->class_dev)) {
++              err = PTR_ERR(data->class_dev);
++              goto exit_remove;
++      }
++
++      return 0;
++
++exit_remove:
++      sysfs_remove_group(&client->dev.kobj, &data->attrs);
++exit_detach:
++      i2c_detach_client(client);
++exit_free:
++      kfree(data);
++exit:
++      return err;
++}
++
++static int ad7418_detach_client(struct i2c_client *client)
++{
++      struct ad7418_data *data = i2c_get_clientdata(client);
++      hwmon_device_unregister(data->class_dev);
++      sysfs_remove_group(&client->dev.kobj, &data->attrs);
++      i2c_detach_client(client);
++      kfree(data);
++      return 0;
++}
++
++static int __init ad7418_init(void)
++{
++      return i2c_add_driver(&ad7418_driver);
++}
++
++static void __exit ad7418_exit(void)
++{
++      i2c_del_driver(&ad7418_driver);
++}
++
++MODULE_AUTHOR("Alessandro Zummo <[EMAIL PROTECTED]>");
++MODULE_DESCRIPTION("AD7416/17/18 driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(DRV_VERSION);
++
++module_init(ad7418_init);
++module_exit(ad7418_exit);
+diff -Naur linux-2.6.21.6/drivers/hwmon/Kconfig 
linux-2.6.21.6_patched/drivers/hwmon/Kconfig
+--- linux-2.6.21.6/drivers/hwmon/Kconfig       2007-07-06 22:47:55.000000000 
-0600
++++ linux-2.6.21.6_patched/drivers/hwmon/Kconfig       2007-09-17 
22:02:54.000000000 -0600
+@@ -575,6 +575,16 @@
+         This driver can also be built as a module.  If so, the module
+         will be called w83627ehf.
+ 
++config SENSORS_AD7418
++      tristate "Analog Devices AD7416/17/18"
++      depends on HWMON && I2C && EXPERIMENTAL
++      help
++        If you say yes here you get support for the Analog Devices
++        AD7416, AD7417 and AD7418 temperature monitoring chips.
++
++        This driver can also be built as a module. If so, the module
++        will be called ad7418.
++
+ config SENSORS_HDAPS
+       tristate "IBM Hard Drive Active Protection System (hdaps)"
+       depends on HWMON && INPUT && X86
+diff -Naur linux-2.6.21.6/drivers/hwmon/Makefile 
linux-2.6.21.6_patched/drivers/hwmon/Makefile
+--- linux-2.6.21.6/drivers/hwmon/Makefile      2007-07-06 22:47:55.000000000 
-0600
++++ linux-2.6.21.6_patched/drivers/hwmon/Makefile      2007-09-17 
22:02:54.000000000 -0600
+@@ -14,6 +14,7 @@
+ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
+ 
+ obj-$(CONFIG_SENSORS_ABITUGURU)       += abituguru.o
++obj-$(CONFIG_SENSORS_AD7418)  += ad7418.o
+ obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
+ obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
+ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
Index: package/kernel/modules/other.mk
===================================================================
--- package/kernel/modules/other.mk     (revision 8834)
+++ package/kernel/modules/other.mk     (working copy)
@@ -370,6 +370,17 @@
 endef
 $(eval $(call KernelPackage,hwmon-pc87360))
 
+define KernelPackage/hwmon-ad7418
+  TITLE:=AD7418 monitoring support
+  DESCRIPTION:=Kernel modules for AD7416/7/8 chips
+  DEFAULT:=y if LINUX_2_6_IXP4XX_Avila
+  SUBMENU:=$(EMENU)
+  KCONFIG:=$(CONFIG_SENSORS_AD7418)
+  FILES:=$(LINUX_DIR)/drivers/hwmon/ad7418.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,50,ad7418)
+endef
+$(eval $(call KernelPackage,hwmon-ad7418))
+
 define KernelPackage/input-core
   TITLE:=Input device core
   DESCRIPTION:=Kernel modules for support of input device
#
# Copyright (C) 2006 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# $Id$

EMENU:=Other modules

define KernelPackage/crypto
  TITLE:=CryptoAPI modules
  KCONFIG:= \
        $(CONFIG_CRYPTO_HMAC) \
        $(CONFIG_CRYPTO_NULL) \
        $(CONFIG_CRYPTO_MD4) \
        $(CONFIG_CRYPTO_MD5) \
        $(CONFIG_CRYPTO_SHA1) \
        $(CONFIG_CRYPTO_SHA256) \
        $(CONFIG_CRYPTO_SHA512) \
        $(CONFIG_CRYPTO_WP512) \
        $(CONFIG_CRYPTO_TGR192) \
        $(CONFIG_CRYPTO_DES) \
        $(CONFIG_CRYPTO_BLOWFISH) \
        $(CONFIG_CRYPTO_TWOFISH) \
        $(CONFIG_CRYPTO_SERPENT) \
        $(CONFIG_CRYPTO_AES) \
        $(CONFIG_CRYPTO_CAST5) \
        $(CONFIG_CRYPTO_CAST6) \
        $(CONFIG_CRYPTO_TEA) \
        $(CONFIG_CRYPTO_ARC4) \
        $(CONFIG_CRYPTO_KHAZAD) \
        $(CONFIG_CRYPTO_ANUBIS) \
        $(CONFIG_CRYPTO_DEFLATE) \
        $(CONFIG_CRYPTO_MICHAEL_MIC) \
        $(CONFIG_CRYPTO_CRC32C) \
        $(CONFIG_CRYPTO_ECB)) \
        $(CONFIG_CRYPTO_BLKCIPHER) 
  FILES:=$(LINUX_DIR)/crypto/*.$(LINUX_KMOD_SUFFIX)
  SUBMENU:=$(EMENU)
endef
$(eval $(call KernelPackage,crypto))

define KernelPackage/ide-core
  TITLE:=Kernel support for IDE
  DESCRIPTION:=\
        Useful for usb mass storage devices (e.g. on WL-HDD)\\\
        \\\
        Includes: \\\
        - ide-core \\\
        - ide-detect \\\
        - ide-disk
  KCONFIG:=$(CONFIG_IDE)
  FILES:=$(LINUX_DIR)/drivers/ide/*.$(LINUX_KMOD_SUFFIX)
  SUBMENU:=$(EMENU)
  AUTOLOAD:=$(call AutoLoad,20,ide-core) $(call AutoLoad,90,ide-generic 
ide-detect ide-disk)
endef
$(eval $(call KernelPackage,ide-core))

define KernelPackage/ide-pdc202xx
  TITLE:=PDC202xx IDE driver
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_BLK_DEV_PDC202XX_OLD)
  FILES:=$(LINUX_DIR)/drivers/ide/pci/pdc202xx_old.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,30,pdc202xx_old)
endef
$(eval $(call KernelPackage,ide-pdc202xx))

define KernelPackage/ide-aec62xx
  TITLE:=AEC62xx IDE driver
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_BLK_DEV_AEC62XX)
  FILES:=$(LINUX_DIR)/drivers/ide/pci/aec62xx.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,30,aec62xx)
endef
$(eval $(call KernelPackage,ide-aec62xx))

define KernelPackage/scsi-core
  TITLE:=Kernel support for SCSI
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_BLK_DEV_SD)
  FILES:= \
    $(LINUX_DIR)/drivers/scsi/scsi_mod.$(LINUX_KMOD_SUFFIX) \
    $(LINUX_DIR)/drivers/scsi/sd_mod.$(LINUX_KMOD_SUFFIX)
endef
$(eval $(call KernelPackage,scsi-core))

define KernelPackage/lp
  TITLE:=Parallel port and line printer support
  KCONFIG:=$(CONFIG_PARPORT)
  FILES:= \
        $(LINUX_DIR)/drivers/parport/parport.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/char/lp.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/char/ppdev.$(LINUX_KMOD_SUFFIX)
  SUBMENU:=$(EMENU)
  AUTOLOAD:=$(call AutoLoad,50, \
        parport \
        lp \
  )
endef
$(eval $(call KernelPackage,lp))


define KernelPackage/soundcore
  TITLE:=Sound support
  DESCRIPTION:=Kernel modules for sound support
  KCONFIG:=$(CONFIG_SOUND)
  SUBMENU:=$(EMENU)
endef

define KernelPackage/soundcore/2.4
  FILES:=$(LINUX_DIR)/drivers/sound/soundcore.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,30,soundcore)
endef

define KernelPackage/soundcore/2.6
  FILES:= \
        $(LINUX_DIR)/sound/soundcore.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/sound/core/*.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/sound/core/oss/*.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,30,soundcore snd snd-page-alloc snd-hwdep 
snd-rawmidi snd-timer snd-pcm snd-mixer-oss snd-pcm-oss)
endef

define KernelPackage/soundcore/uml-2.6
  FILES:= \
        $(LINUX_DIR)/arch/um/drivers/hostaudio.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/sound/soundcore.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,30,soundcore hostaudio)
endef

$(eval $(call KernelPackage,soundcore))


define KernelPackage/loop
  TITLE:=Loopback device support
  DESCRIPTION:=Kernel module for loopback device support
  KCONFIG:=$(CONFIG_BLK_DEV_LOOP)
  SUBMENU:=$(EMENU)
  AUTOLOAD:=$(call AutoLoad,30,loop)
  FILES:=$(LINUX_DIR)/drivers/block/loop.$(LINUX_KMOD_SUFFIX)
endef
$(eval $(call KernelPackage,loop))

define KernelPackage/nbd
  TITLE:=Network block device support
  DESCRIPTION:=Kernel module for network block device support
  KCONFIG:=$(CONFIG_BLK_DEV_NBD)
  SUBMENU:=$(EMENU)
  AUTOLOAD:=$(call AutoLoad,30,nbd)
  FILES:=$(LINUX_DIR)/drivers/block/nbd.$(LINUX_KMOD_SUFFIX)
endef
$(eval $(call KernelPackage,nbd))

define KernelPackage/capi
  TITLE:=CAPI Support
  DESCRIPTION:=Kernel module for basic CAPI support
  KCONFIG:=$(CONFIG_ISDN) $(CONFIG_ISDN_CAPI) $(CONFIG_ISDN_CAPI_CAPI20)
  SUBMENU:=$(EMENU)
  AUTOLOAD:=$(call AutoLoad,30,kernelcapi capi)
  FILES:=$(LINUX_DIR)/drivers/isdn/capi/*capi.$(LINUX_KMOD_SUFFIX)
endef
$(eval $(call KernelPackage,capi))

define KernelPackage/pcmcia-core
  TITLE:=PCMCIA/CardBus support
  DESCRIPTION:=Kernel support for PCMCIA/CardBus controllers
  SUBMENU:=$(EMENU)
endef

define KernelPackage/pcmcia-core/2.4
  KCONFIG:=$(CONFIG_PCMCIA)
  FILES:= \
        $(LINUX_DIR)/drivers/pcmcia/pcmcia_core.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/pcmcia/yenta_socket.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/pcmcia/ds.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,40,pcmcia_core yenta_socket ds)
endef

define KernelPackage/pcmcia-core/2.6
  KCONFIG:=$(CONFIG_PCCARD)
  FILES:= \
        $(LINUX_DIR)/drivers/pcmcia/pcmcia_core.$(LINUX_KMOD_SUFFIX) \
        $(if 
$(CONFIG_PCMCIA),$(LINUX_DIR)/drivers/pcmcia/pcmcia.$(LINUX_KMOD_SUFFIX)) \
        $(LINUX_DIR)/drivers/pcmcia/yenta_socket.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/pcmcia/rsrc_nonstatic.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,40,pcmcia_core pcmcia rsrc_nonstatic yenta_socket)
endef
$(eval $(call KernelPackage,pcmcia-core))


define KernelPackage/pcmcia-serial
  TITLE:=Serial devices support
  DESCRIPTION:=Kernel support for PCMCIA/CardBus serial devices
  DEPENDS:=kmod-pcmcia-core
  SUBMENU:=$(EMENU)
  AUTOLOAD:=$(call AutoLoad,45,serial_cs)
endef

define KernelPackage/pcmcia-serial/2.4
  KCONFIG:=$(CONFIG_PCMCIA_SERIAL_CS)
  FILES:=$(LINUX_DIR)/drivers/char/pcmcia/serial_cs.$(LINUX_KMOD_SUFFIX)
endef

define KernelPackage/pcmcia-serial/2.6
  KCONFIG:=$(CONFIG_SERIAL_8250_CS)
  FILES:=$(LINUX_DIR)/drivers/serial/serial_cs.$(LINUX_KMOD_SUFFIX)
endef
$(eval $(call KernelPackage,pcmcia-serial))


define KernelPackage/bluetooth
  TITLE:=Bluetooth support
  DEPENDS:[EMAIL PROTECTED]
  DESCRIPTION:=Kernel support for Bluetooth devices
  SUBMENU:=$(EMENU)
 endef

 define KernelPackage/bluetooth/2.4
  KCONFIG:=$(CONFIG_BLUEZ)
  FILES:= \
        $(LINUX_DIR)/net/bluetooth/bluez.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/l2cap.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/sco.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/rfcomm/rfcomm.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/bnep/bnep.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/bluetooth/hci_uart.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/bluetooth/hci_usb.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,90,bluez l2cap sco rfcomm bnep hci_uart hci_usb)
endef

define KernelPackage/bluetooth/2.6
  KCONFIG:=$(CONFIG_BT)
  FILES:= \
        $(LINUX_DIR)/net/bluetooth/bluetooth.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/l2cap.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/sco.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/rfcomm/rfcomm.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/net/bluetooth/bnep/bnep.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/bluetooth/hci_uart.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/bluetooth/hci_usb.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,90,bluetooth l2cap sco rfcomm bnep hci_uart hci_usb)
endef
$(eval $(call KernelPackage,bluetooth))

define KernelPackage/mmc
  TITLE:=MMC/SD Card Support
  DEPENDS:[EMAIL PROTECTED]
  DESCRIPTION:=Kernel support for MMC/SD cards
  SUBMENU:=$(EMENU)
 endef

define KernelPackage/mmc/2.6
        KCONFIG:=$(CONFIG_MMC)
        FILES:= \
        $(LINUX_DIR)/drivers/mmc/mmc_core.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/mmc/mmc_block.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/mmc/at91_mci.$(LINUX_KMOD_SUFFIX)
        AUTOLOAD:=$(call AutoLoad,90,mmc_core mmc_block at91_mci)
endef
$(eval $(call KernelPackage,mmc))

define KernelPackage/softdog
  TITLE:=Software watchdog driver
  DESCRIPTION:=Software watchdog driver
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_SOFT_WATCHDOG)
  FILES:=$(LINUX_DIR)/drivers/char/softdog.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,softdog)
endef

define KernelPackage/softdog/2.4
  FILES:=$(LINUX_DIR)/drivers/char/softdog.o
endef

define KernelPackage/softdog/2.6
  FILES:=$(LINUX_DIR)/drivers/char/watchdog/softdog.ko
endef

$(eval $(call KernelPackage,softdog))


define KernelPackage/videodev
  TITLE=Video4Linux support
  DESCRIPTION:=Kernel modules for Video4Linux support
  DEPENDS:[EMAIL PROTECTED]
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_VIDEO_DEV)
  FILES:=$(LINUX_DIR)/drivers/media/video/*.$(LINUX_KMOD_SUFFIX)
endef

define KernelPackage/videodev/2.4
  AUTOLOAD:=$(call AutoLoad,60,videodev)
endef

define KernelPackage/videodev/2.6
  AUTOLOAD:=$(call AutoLoad,60,v4l2-common v4l1-compat compat_ioctl32 videodev)
endef
$(eval $(call KernelPackage,videodev))

define KernelPackage/leds-net48xx
  TITLE:=Soekris Net48xx LED support
  DESCRIPTION:=Kernel module for Soekris Net48xx LEDs
  DEFAULT:=y if LINUX_2_6_X86_Soekris
  DEPENDS:[EMAIL PROTECTED]
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_LEDS_NET48XX)
  FILES:=$(LINUX_DIR)/drivers/leds/leds-net48xx.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,leds-net48xx)
endef
$(eval $(call KernelPackage,leds-net48xx))

define KernelPackage/nsc-gpio
  TITLE:=Natsemi GPIO support
  DESCRIPTION:=Kernel module for Natsemi GPIO
  DEFAULT:=y if LINUX_2_6_X86_Soekris
  DEPENDS:[EMAIL PROTECTED]
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_NSC_GPIO)
  FILES:=$(LINUX_DIR)/drivers/char/nsc_gpio.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,40,nsc_gpio)
endef
$(eval $(call KernelPackage,nsc-gpio))

define KernelPackage/scx200-gpio
  TITLE:=Natsemi SCX200 GPIO support
  DESCRIPTION:=Kernel module for SCX200 GPIO
  DEFAULT:=y if LINUX_2_6_X86_Soekris
  DEPENDS:=kmod-nsc-gpio @LINUX_2_6_X86_Soekris
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_SCx200_GPIO)
  FILES:=$(LINUX_DIR)/drivers/char/scx200_gpio.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,scx200_gpio)
endef
$(eval $(call KernelPackage,scx200-gpio))

define KernelPackage/scx200-wdt
  TITLE:=Natsemi SCX200 Watchdog support
  DESCRIPTION:=Kernel module for SCX200 Watchdog
  DEFAULT:=y if LINUX_2_6_X86_Soekris
  DEPENDS:[EMAIL PROTECTED]
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_SC1200_WDT)
  FILES:=$(LINUX_DIR)/drivers/char/watchdog/scx200_wdt.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,scx200_wdt)
endef
$(eval $(call KernelPackage,scx200-wdt))

define KernelPackage/hwmon
  TITLE:=Hardware monitoring support
  DESCRIPTION:=Kernel modules for hardware monitoring
  DEFAULT:=y if LINUX_2_6_X86_Soekris
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_HWMON_VID)
  FILES:= \
        $(LINUX_DIR)/drivers/hwmon/hwmon.$(LINUX_KMOD_SUFFIX) \
        $(LINUX_DIR)/drivers/hwmon/hwmon-vid.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,40,hwmon hwmon-vid)
endef
$(eval $(call KernelPackage,hwmon))

define KernelPackage/hwmon-pc87360
  TITLE:=PC87360 monitoring support
  DESCRIPTION:=Kernel modules for PC87360 chips
  DEFAULT:=y if LINUX_2_6_X86_Soekris
  DEPENDS:=kmod-hwmon
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_SENSORS_PC87360)
  FILES:=$(LINUX_DIR)/drivers/hwmon/pc87360.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,pc87360)
endef
$(eval $(call KernelPackage,hwmon-pc87360))

define KernelPackage/hwmon-ad7418
  TITLE:=AD7418 monitoring support
  DESCRIPTION:=Kernel modules for AD7416/7/8 chips
  DEFAULT:=y if LINUX_2_6_IXP4XX_Avila
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_SENSORS_AD7418)
  FILES:=$(LINUX_DIR)/drivers/hwmon/ad7418.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,ad7418)
endef
$(eval $(call KernelPackage,hwmon-ad7418))

define KernelPackage/input-core
  TITLE:=Input device core
  DESCRIPTION:=Kernel modules for support of input device
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_INPUT)
  FILES:=$(LINUX_DIR)/drivers/input/input-core.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,50,input-core)
endef
$(eval $(call KernelPackage,input-core))

define KernelPackage/input-evdev
  TITLE:=Input even device
  DESCRIPTION:=Kernel modules for support of input device events
  DEPENDS:=+kmod-input-core
  SUBMENU:=$(EMENU)
  KCONFIG:=$(CONFIG_INPUT_EVDEV)
  FILES:=$(LINUX_DIR)/drivers/input/evdev.$(LINUX_KMOD_SUFFIX)
  AUTOLOAD:=$(call AutoLoad,60,evdev)
endef
$(eval $(call KernelPackage,input-evdev))

diff -Naur linux-2.6.21.6/drivers/hwmon/ad7418.c 
linux-2.6.21.6_patched/drivers/hwmon/ad7418.c
--- linux-2.6.21.6/drivers/hwmon/ad7418.c       1969-12-31 17:00:00.000000000 
-0700
+++ linux-2.6.21.6_patched/drivers/hwmon/ad7418.c       2007-09-17 
22:03:43.000000000 -0600
@@ -0,0 +1,373 @@
+/*
+ * An hwmon driver for the Analog Devices AD7416/17/18
+ * Copyright (C) 2006-07 Tower Technologies
+ *
+ * Author: Alessandro Zummo <[EMAIL PROTECTED]>
+ *
+ * Based on lm75.c
+ * Copyright (C) 1998-99 Frodo Looijaard <[EMAIL PROTECTED]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License,
+ * as published by the Free Software Foundation - version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include "lm75.h"
+
+#define DRV_VERSION "0.3"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END };
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_3(ad7416, ad7417, ad7418);
+
+/* AD7418 registers */
+#define AD7418_REG_TEMP_IN     0x00
+#define AD7418_REG_CONF                0x01
+#define AD7418_REG_TEMP_HYST   0x02
+#define AD7418_REG_TEMP_OS     0x03
+#define AD7418_REG_ADC         0x04
+#define AD7418_REG_CONF2       0x05
+
+#define AD7418_REG_ADC_CH(x)   ((x) << 5)
+#define AD7418_CH_TEMP         AD7418_REG_ADC_CH(0)
+
+static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
+                                       AD7418_REG_TEMP_HYST,
+                                       AD7418_REG_TEMP_OS };
+
+struct ad7418_data {
+       struct i2c_client       client;
+       struct class_device     *class_dev;
+       struct attribute_group  attrs;
+       enum chips              type;
+       struct mutex            lock;
+       int                     adc_max;        /* number of ADC channels */
+       char                    valid;
+       unsigned long           last_updated;   /* In jiffies */
+       s16                     temp[3];        /* Register values */
+       u16                     in[4];
+};
+
+static int ad7418_attach_adapter(struct i2c_adapter *adapter);
+static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind);
+static int ad7418_detach_client(struct i2c_client *client);
+
+static struct i2c_driver ad7418_driver = {
+       .driver = {
+               .name   = "ad7418",
+       },
+       .attach_adapter = ad7418_attach_adapter,
+       .detach_client  = ad7418_detach_client,
+};
+
+/* All registers are word-sized, except for the configuration registers.
+ * AD7418 uses a high-byte first convention. Do NOT use those functions to
+ * access the configuration registers CONF and CONF2, as they are byte-sized.
+ */
+static inline int ad7418_read(struct i2c_client *client, u8 reg)
+{
+       return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value)
+{
+       return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+
+static void ad7418_init_client(struct i2c_client *client)
+{
+       struct ad7418_data *data = i2c_get_clientdata(client);
+
+       int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
+       if (reg < 0) {
+               dev_err(&client->dev, "cannot read configuration register\n");
+       } else {
+               dev_info(&client->dev, "configuring for mode 1\n");
+               i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe);
+
+               if (data->type == ad7417 || data->type == ad7418)
+                       i2c_smbus_write_byte_data(client,
+                                               AD7418_REG_CONF2, 0x00);
+       }
+}
+
+static struct ad7418_data *ad7418_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ad7418_data *data = i2c_get_clientdata(client);
+
+       mutex_lock(&data->lock);
+
+       if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+               || !data->valid) {
+               u8 cfg;
+               int i, ch;
+
+               /* read config register and clear channel bits */
+               cfg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
+               cfg &= 0x1F;
+
+               i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
+                                               cfg | AD7418_CH_TEMP);
+               udelay(30);
+
+               for (i = 0; i < 3; i++) {
+                       data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]);
+               }
+
+               for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
+                       i2c_smbus_write_byte_data(client,
+                                       AD7418_REG_CONF,
+                                       cfg | AD7418_REG_ADC_CH(ch));
+
+                       udelay(15);
+                       data->in[data->adc_max - 1 - i] =
+                               ad7418_read(client, AD7418_REG_ADC);
+               }
+
+               /* restore old configuration value */
+               ad7418_write(client, AD7418_REG_CONF, cfg);
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->lock);
+
+       return data;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct ad7418_data *data = ad7418_update_device(dev);
+       return sprintf(buf, "%d\n",
+               LM75_TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static ssize_t show_adc(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct ad7418_data *data = ad7418_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               ((data->in[attr->index] >> 6) * 2500 + 512) / 1024);
+}
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
+                       const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ad7418_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+
+       mutex_lock(&data->lock);
+       data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
+       ad7418_write(client, AD7418_REG_TEMP[attr->index], 
data->temp[attr->index]);
+       mutex_unlock(&data->lock);
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+                               show_temp, set_temp, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+                               show_temp, set_temp, 2);
+
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3);
+
+static int ad7418_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_probe(adapter, &addr_data, ad7418_detect);
+}
+
+static struct attribute *ad7416_attributes[] = {
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *ad7417_attributes[] = {
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in4_input.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *ad7418_attributes[] = {
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       NULL
+};
+
+static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct ad7418_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+                                       I2C_FUNC_SMBUS_WORD_DATA))
+               goto exit;
+
+       if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       client = &data->client;
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &ad7418_driver;
+
+       i2c_set_clientdata(client, data);
+
+       mutex_init(&data->lock);
+
+       /* AD7418 has a curious behaviour on registers 6 and 7. They
+        * both always read 0xC071 and are not documented on the datasheet.
+        * We use them to detect the chip.
+        */
+       if (kind <= 0) {
+               int reg, reg6, reg7;
+
+               /* the AD7416 lies within this address range, but I have
+                * no means to check.
+                */
+               if (address >= 0x48 && address <= 0x4f) {
+                       /* XXX add tests for AD7416 here */
+                       /* data->type = ad7416; */
+               }
+               /* here we might have AD7417 or AD7418 */
+               else if (address >= 0x28 && address <= 0x2f) {
+                       reg6 = i2c_smbus_read_word_data(client, 0x06);
+                       reg7 = i2c_smbus_read_word_data(client, 0x07);
+
+                       if (address == 0x28 && reg6 == 0xC071 && reg7 == 0xC071)
+                               data->type = ad7418;
+
+                       /* XXX add tests for AD7417 here */
+
+
+                       /* both AD7417 and AD7418 have bits 0-5 of
+                        * the CONF2 register at 0
+                        */
+                       reg = i2c_smbus_read_byte_data(client,
+                                                       AD7418_REG_CONF2);
+                       if (reg & 0x3F)
+                               data->type = any_chip; /* detection failed */
+               }
+       } else {
+               dev_dbg(&adapter->dev, "detection forced\n");
+       }
+
+       if (kind > 0)
+               data->type = kind;
+       else if (kind < 0 && data->type == any_chip) {
+               err = -ENODEV;
+               goto exit_free;
+       }
+
+       switch (data->type) {
+       case any_chip:
+       case ad7416:
+               data->adc_max = 0;
+               data->attrs.attrs = ad7416_attributes;
+               strlcpy(client->name, "ad7416", I2C_NAME_SIZE);
+               break;
+
+       case ad7417:
+               data->adc_max = 4;
+               data->attrs.attrs = ad7417_attributes;
+               strlcpy(client->name, "ad7417", I2C_NAME_SIZE);
+               break;
+
+       case ad7418:
+               data->adc_max = 1;
+               data->attrs.attrs = ad7418_attributes;
+               strlcpy(client->name, "ad7418", I2C_NAME_SIZE);
+               break;
+       }
+
+       if ((err = i2c_attach_client(client)))
+               goto exit_free;
+
+       dev_info(&client->dev, "%s chip found\n", client->name);
+
+       /* Initialize the AD7418 chip */
+       ad7418_init_client(client);
+
+       /* Register sysfs hooks */
+       if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
+               goto exit_detach;
+
+       data->class_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->class_dev)) {
+               err = PTR_ERR(data->class_dev);
+               goto exit_remove;
+       }
+
+       return 0;
+
+exit_remove:
+       sysfs_remove_group(&client->dev.kobj, &data->attrs);
+exit_detach:
+       i2c_detach_client(client);
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static int ad7418_detach_client(struct i2c_client *client)
+{
+       struct ad7418_data *data = i2c_get_clientdata(client);
+       hwmon_device_unregister(data->class_dev);
+       sysfs_remove_group(&client->dev.kobj, &data->attrs);
+       i2c_detach_client(client);
+       kfree(data);
+       return 0;
+}
+
+static int __init ad7418_init(void)
+{
+       return i2c_add_driver(&ad7418_driver);
+}
+
+static void __exit ad7418_exit(void)
+{
+       i2c_del_driver(&ad7418_driver);
+}
+
+MODULE_AUTHOR("Alessandro Zummo <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("AD7416/17/18 driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(ad7418_init);
+module_exit(ad7418_exit);
diff -Naur linux-2.6.21.6/drivers/hwmon/Kconfig 
linux-2.6.21.6_patched/drivers/hwmon/Kconfig
--- linux-2.6.21.6/drivers/hwmon/Kconfig        2007-07-06 22:47:55.000000000 
-0600
+++ linux-2.6.21.6_patched/drivers/hwmon/Kconfig        2007-09-17 
22:02:54.000000000 -0600
@@ -575,6 +575,16 @@
          This driver can also be built as a module.  If so, the module
          will be called w83627ehf.
 
+config SENSORS_AD7418
+       tristate "Analog Devices AD7416/17/18"
+       depends on HWMON && I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for the Analog Devices
+         AD7416, AD7417 and AD7418 temperature monitoring chips.
+
+         This driver can also be built as a module. If so, the module
+         will be called ad7418.
+
 config SENSORS_HDAPS
        tristate "IBM Hard Drive Active Protection System (hdaps)"
        depends on HWMON && INPUT && X86
diff -Naur linux-2.6.21.6/drivers/hwmon/Makefile 
linux-2.6.21.6_patched/drivers/hwmon/Makefile
--- linux-2.6.21.6/drivers/hwmon/Makefile       2007-07-06 22:47:55.000000000 
-0600
+++ linux-2.6.21.6_patched/drivers/hwmon/Makefile       2007-09-17 
22:02:54.000000000 -0600
@@ -14,6 +14,7 @@
 obj-$(CONFIG_SENSORS_W83791D)  += w83791d.o
 
 obj-$(CONFIG_SENSORS_ABITUGURU)        += abituguru.o
+obj-$(CONFIG_SENSORS_AD7418)   += ad7418.o
 obj-$(CONFIG_SENSORS_ADM1021)  += adm1021.o
 obj-$(CONFIG_SENSORS_ADM1025)  += adm1025.o
 obj-$(CONFIG_SENSORS_ADM1026)  += adm1026.o
_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
http://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

Reply via email to