Index: linux-2.6/drivers/hwmon/Kconfig
===================================================================
--- linux-2.6.orig/drivers/hwmon/Kconfig	2006-03-22 22:03:24.000000000 +0100
+++ linux-2.6/drivers/hwmon/Kconfig	2006-03-25 15:33:24.000000000 +0100
@@ -315,6 +315,16 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called pc87360.
 
+config SENSORS_S3C_ADC
+	tristate "S3C2410/S3C2440 Inbuilt ADC"
+	depends on HWMON && ARCH_S3C2410
+	help
+	  If you say yes here you get support for the on-board ADCs of
+	  the S3C2410 and S3C2440 SoCs.
+
+	  This driver can also be built as a module. If so, the modul;e
+	  will be called s3c24xx-adc.
+
 config SENSORS_SIS5595
 	tristate "Silicon Integrated Systems Corp. SiS5595"
 	depends on HWMON && I2C && PCI && EXPERIMENTAL
Index: linux-2.6/drivers/hwmon/Makefile
===================================================================
--- linux-2.6.orig/drivers/hwmon/Makefile	2006-03-22 22:03:24.000000000 +0100
+++ linux-2.6/drivers/hwmon/Makefile	2006-03-25 15:33:24.000000000 +0100
@@ -44,6 +44,7 @@
 obj-$(CONFIG_SENSORS_VT8231)	+= vt8231.o
 obj-$(CONFIG_SENSORS_W83627EHF)	+= w83627ehf.o
 obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
+obj-$(CONFIG_SENSORS_S3C_ADC)	+= s3c24xx-adc.o
 
 ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
Index: linux-2.6/drivers/hwmon/lm75.c
===================================================================
--- linux-2.6.orig/drivers/hwmon/lm75.c	2006-03-22 22:03:24.000000000 +0100
+++ linux-2.6/drivers/hwmon/lm75.c	2006-03-25 15:33:24.000000000 +0100
@@ -33,7 +33,7 @@
 					0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm75);
+I2C_CLIENT_INSMOD_2(lm75, lm75_compat);
 
 /* Many LM75 constants specified below */
 
@@ -167,22 +167,41 @@
 
 		/* Unused bits */
 		if (conf & 0xe0)
-		 	goto exit_free;
+		 	goto not_real_lm75;
 
 		/* Addresses cycling */
 		for (i = 8; i < 0xff; i += 8)
 			if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
 			 || i2c_smbus_read_word_data(new_client, i + 2) != hyst
 			 || i2c_smbus_read_word_data(new_client, i + 3) != os)
-				goto exit_free;
+			{
+				/* check to see if this is compatible */
+				
+			not_real_lm75:
+				os = i2c_smbus_read_word_data(new_client, 3);
+				
+				for (i = 8; i < 0xff; i += 8) {
+					
+					if (i2c_smbus_read_word_data(new_client, i) != os)
+						goto exit_free;
+				}
+				
+				kind = lm75_compat;
+			}
 	}
 
 	/* Determine the chip type - only one kind supported! */
 	if (kind <= 0)
 		kind = lm75;
 
-	if (kind == lm75) {
+	switch (kind) {
+	case lm75:
 		name = "lm75";
+		break;
+
+	case lm75_compat:
+		name = "lm75_compatible";
+		break;
 	}
 
 	/* Fill in the remaining client fields and put it into the global list */
Index: linux-2.6/drivers/hwmon/s3c24xx-adc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/hwmon/s3c24xx-adc.c	2006-03-25 15:37:00.000000000 +0100
@@ -0,0 +1,424 @@
+/* linux/drivers/hwmon/s3c24xx-adc.c
+ *
+ * Copyright (C) 2005 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	Shannon Holland <holland@loser.net>
+ *
+ * S3C2410/S3C2440 ADC converter support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+//#define DEBUG
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <asm/hardware/clock.h>
+#include <asm/io.h>
+
+#include <asm/arch/regs-adc.h>
+#include <asm/arch/regs-irq.h>
+#include <asm/arch/adc.h>
+/* Private data */
+
+struct s3c24xx_adc {
+	struct semaphore	lock;
+	wait_queue_head_t	wait;
+
+	int			val;
+	int			irq;
+	void __iomem		*regs;
+	struct clk		*clk;
+	struct device		*dev;
+	struct class_device	*hwmon_dev;
+	struct resource		*ioarea;
+};
+
+static inline struct s3c24xx_adc *dev_to_adc(struct device *dev)
+{
+	return (struct s3c24xx_adc *)dev_get_drvdata(dev);
+}
+
+static int s3c24xx_adc_read(struct s3c24xx_adc *adc, int channel)
+{
+	unsigned long timeout;
+	unsigned long con;
+	int ret;
+
+	ret = down_interruptible(&adc->lock);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(adc->dev, "reading channel %d\n", channel);
+
+	adc->val = -1;
+
+	clk_enable(adc->clk);
+
+	con = readl(adc->regs + S3C2410_ADCCON);
+
+	con &= ~S3C2410_ADCCON_MUXMASK;
+	con |=  S3C2410_ADCCON_SELMUX(channel);
+	con &= ~S3C2410_ADCCON_STDBM;
+	
+	dev_dbg(adc->dev, "CON setting now %08lx\n", con);
+
+	/* bring the adc out of standby first */
+	writel(con, adc->regs + S3C2410_ADCCON);
+
+	/* enable the pre-scaler */
+	con |= S3C2410_ADCCON_PRSCEN;
+	writel(con, adc->regs + S3C2410_ADCCON);
+
+	/* start the conversion */
+	con |=  S3C2410_ADCCON_ENABLE_START;
+	writel(con, adc->regs + S3C2410_ADCCON);
+	
+	timeout = wait_event_timeout(adc->wait, adc->val >= 0, HZ / 2);
+	ret = (timeout == 0) ? -ETIMEDOUT : adc->val;
+
+	up(&adc->lock);
+
+	return ret;
+}
+
+
+static ssize_t s3c24xx_adc_showraw(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct s3c24xx_adc *adc = dev_to_adc(dev);
+	int nr;
+	int ret;
+
+	nr = attr->attr.name[3] - '0';
+
+	ret = s3c24xx_adc_read(adc, nr);
+
+	if (ret < 0) 
+		return ret;
+	else
+		return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+#define DEF_ADC_ATTR(x)							\
+static DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c24xx_adc_showraw, NULL)
+
+DEF_ADC_ATTR(0);
+DEF_ADC_ATTR(1);
+DEF_ADC_ATTR(2);
+DEF_ADC_ATTR(3);
+DEF_ADC_ATTR(4);
+DEF_ADC_ATTR(5);
+DEF_ADC_ATTR(6);
+DEF_ADC_ATTR(7);
+
+static struct device_attribute *s3c2410_adc_attrs[8] = {
+	&dev_attr_adc0_raw,
+	&dev_attr_adc1_raw,
+	&dev_attr_adc2_raw,
+	&dev_attr_adc3_raw,
+	&dev_attr_adc4_raw,
+	&dev_attr_adc5_raw,
+	&dev_attr_adc6_raw,
+	&dev_attr_adc7_raw,
+};
+
+static irqreturn_t s3c24xx_adc_irq(int irqno, void *dev_id,
+				   struct pt_regs *regs)
+{
+	struct s3c24xx_adc *adc = dev_id;
+	unsigned long con;
+
+	adc->val  = readl(adc->regs + S3C2410_ADCDAT0);
+	adc->val &= S3C2410_ADCDAT0_XPDATA_MASK;
+
+	dev_dbg(adc->dev, "read %08x from adc (con %08x)\n",
+		adc->val, readl(adc->regs + S3C2410_ADCCON));
+
+	/* place the adc back into standby mode */
+
+	con  = readl(adc->regs + S3C2410_ADCCON);
+	con |= S3C2410_ADCCON_STDBM;
+	con &= ~S3C2410_ADCCON_PRSCEN;
+	writel(con, adc->regs + S3C2410_ADCCON);
+
+	wake_up(&adc->wait);
+	return IRQ_HANDLED;
+}
+
+
+static int s3c24xx_adc_remove(struct device *dev)
+{
+	struct s3c24xx_adc *adc = dev_to_adc(dev);
+
+	if (!IS_ERR(adc->hwmon_dev) && adc->hwmon_dev != NULL) {
+		hwmon_device_unregister(adc->hwmon_dev);
+		adc->hwmon_dev = NULL;
+	}
+
+	if (!IS_ERR(adc->clk) && adc->clk != NULL) {
+		clk_disable(adc->clk);
+		clk_unuse(adc->clk);
+		clk_put(adc->clk);
+		adc->clk = NULL;
+	}
+
+	if (adc->regs != NULL) {
+		iounmap(adc->regs);
+		adc->regs = NULL;
+	}
+
+	if (adc->ioarea != NULL) {
+		release_resource(adc->ioarea);
+		kfree(adc->ioarea);
+		adc->ioarea = NULL;
+	}
+
+	return 0;
+}
+
+static void s3c24xx_adc_setup(struct s3c24xx_adc *adc)
+{
+	/* configure the adc */
+
+	writel(S3C2410_ADCCON_PRSCVL(clk_get_rate(adc->clk) / 1000000) |
+	       S3C2410_ADCCON_STDBM,
+	       adc->regs + S3C2410_ADCCON);
+}
+
+static ssize_t s3c24xx_adc_hwmon_in_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
+	struct s3c24xx_adc_platdata *pdata = dev->platform_data;
+	struct s3c24xx_adc_hwmon_incfg *cfg;
+	struct s3c24xx_adc *adc = dev_to_adc(dev);
+	int ret;
+
+	cfg = pdata->in[sen_attr->index];
+
+	ret = s3c24xx_adc_read(adc, cfg->channel);
+	if (ret < 0)
+		return ret;
+
+	ret *= cfg->mult;
+	ret /= cfg->div;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static int s3c24xx_adc_create_hwmon_in(struct s3c24xx_adc *adc,
+				       struct s3c24xx_adc_platdata *pdata,
+				       int channel)
+{
+	struct sensor_device_attribute *attr;
+
+	if (pdata->in[channel] == NULL)
+		return 0;
+
+	attr = kmalloc(sizeof(*attr) + 16, GFP_KERNEL);
+	if (attr == NULL)
+		return -ENOMEM;
+
+	snprintf((char *)(attr+1), 16, "in_%d", channel);
+
+	memset(attr, 0, sizeof(*attr));
+
+	attr->dev_attr.attr.name  = (char *)(attr+1);
+	attr->dev_attr.attr.mode  = S_IRUGO;
+	attr->dev_attr.attr.owner = THIS_MODULE;
+
+	attr->dev_attr.show = s3c24xx_adc_hwmon_in_show;
+	
+	attr->index = channel;
+
+	return device_create_file(adc->dev, &attr->dev_attr);
+}
+
+static int s3c24xx_adc_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s3c24xx_adc_platdata *pdata = dev->platform_data;
+	struct s3c24xx_adc *adc;
+	struct resource *res;
+	size_t size;
+	int ret;
+	int i;
+
+	adc = kmalloc(sizeof(struct s3c24xx_adc), GFP_KERNEL);
+	if (adc == NULL) {
+		dev_err(dev, "no memory\n");
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(dev, adc);
+
+	/* get our clock and use it */
+
+	adc->dev = dev;
+	adc->clk = clk_get(dev, "adc");
+
+	if (IS_ERR(adc->clk) || adc->clk == NULL) {
+		dev_err(dev, "cannot get clock\n");
+		ret = -ENOENT;
+		goto out_err;
+	}
+
+	clk_use(adc->clk);
+	/* only enable the clock when we are actually using the adc */
+
+	init_waitqueue_head(&adc->wait);
+	init_MUTEX(&adc->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(dev, "no register resoruce\n");
+		ret = -ENOENT;
+		goto out_err;
+	}
+
+	size = (res->end - res->start) + 1;
+
+	adc->ioarea = request_mem_region(res->start, size, pdev->name);
+
+	if (adc->ioarea == NULL) {
+		dev_err(dev, "cannot request IO\n");
+		ret = -ENXIO;
+		goto out_err;
+	}
+
+	adc->regs = ioremap(res->start, size);
+	if (adc->regs == NULL) {
+		dev_err(dev, "cannot map IO\n");
+		ret = -ENXIO;
+		goto out_err;
+	}
+
+	adc->irq = platform_get_irq(pdev, 0);
+	if (adc->irq <= 0) {
+		dev_err(dev, "No IRQ specified\n");
+		ret = -ENXIO;
+		goto out_err;
+	}
+
+	ret = request_irq(adc->irq+1, s3c24xx_adc_irq, 0, pdev->name, adc);
+
+	if (ret != 0) {
+		dev_err(dev, "cannot claim IRQ\n");
+		goto out_err;
+	}
+
+	/* register with the hwmon core */
+
+	adc->hwmon_dev = hwmon_device_register(dev);
+	if (IS_ERR(adc->hwmon_dev)) {
+		dev_err(dev, "cannot register with hwmon\n");
+		goto out_err;
+	}
+
+	s3c24xx_adc_setup(adc);
+
+	/* got registers */
+
+	for (i = 0; i < ARRAY_SIZE(s3c2410_adc_attrs); i++) {
+		device_create_file(dev, s3c2410_adc_attrs[i]);
+	}
+	
+	if (pdata != NULL) {
+		for (i = 0; i < ARRAY_SIZE(pdata->in); i++) {
+			s3c24xx_adc_create_hwmon_in(adc, pdata, i);
+		}
+	}
+
+	return 0;
+
+ out_err:
+	/* detach */
+
+	s3c24xx_adc_remove(dev);
+	return ret;
+}
+
+
+#ifdef CONFIG_PM
+static int s3c24xx_adc_suspend(struct device *dev, pm_message_t state)
+{
+	struct s3c24xx_adc *adc = dev_get_drvdata(dev);
+	unsigned long con;
+
+	con  = readl(adc->regs + S3C2410_ADCCON);
+	con |= S3C2410_ADCCON_STDBM;
+	con &= ~S3C2410_ADCCON_PRSCEN;
+	writel(con, adc->regs + S3C2410_ADCCON);
+	
+	clk_disable(adc->clk);
+
+	return 0;
+}
+
+static int s3c24xx_adc_resume(struct device *dev)
+{
+	struct s3c24xx_adc *adc = dev_get_drvdata(dev);
+	
+	if (adc != NULL) {
+		dev_dbg(dev, "resume: level %d\n", level);
+		s3c24xx_adc_setup(adc);
+	}
+
+	return 0;
+}
+
+#else
+#define s3c24xx_i2c_suspend NULL
+#define s3c24xx_i2c_resume NULL
+#endif
+
+static struct device_driver s3c2410_adc_driver = {
+	.name		= "s3c2410-adc",
+	.owner		= THIS_MODULE,
+	.bus		= &platform_bus_type,
+	.probe		= s3c24xx_adc_probe,
+	.remove		= s3c24xx_adc_remove,
+	.resume		= s3c24xx_adc_resume,
+	.suspend	= s3c24xx_adc_suspend,
+};
+
+static int __init s3c24xx_adc_init(void)
+{
+	return driver_register(&s3c2410_adc_driver);
+}
+
+static void __exit s3c24xx_adc_exit(void)
+{
+	driver_unregister(&s3c2410_adc_driver);
+}
+
+module_init(s3c24xx_adc_init);
+module_exit(s3c24xx_adc_exit);
+
+MODULE_LICENSE("GPL");
Index: linux-2.6/include/asm-arm/arch-s3c2410/adc.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/include/asm-arm/arch-s3c2410/adc.h	2006-03-25 15:33:24.000000000 +0100
@@ -0,0 +1,33 @@
+/* linux/include/asm-arm/arch-s3c2410/adc.h
+ *
+ * (c) 2005 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	http://armlinux.simtec.co.uk/
+ *
+ * S3C2410 - HWMon interface for ADC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Changelog:
+ *	06-Sep-2005 BJD  Created file
+*/
+
+#ifndef __ASM_ARCH_ADC_H
+#define __ASM_ARCH_ADC_H __FILE__
+
+struct s3c24xx_adc_hwmon_incfg {
+	unsigned int		channel;
+	unsigned int		min;
+	unsigned int		max;
+	unsigned int		mult;	/* scale factors */
+	unsigned int		div;
+};
+
+struct s3c24xx_adc_platdata {
+	struct s3c24xx_adc_hwmon_incfg	*in[8];
+};
+
+#endif /* __ASM_ARCH_ADC_H */
+

