All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Dooks <ben@simtec.co.uk>
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] HWMON: S3C24XX series ADC driver
Date: Wed, 20 May 2009 21:50:29 +0000	[thread overview]
Message-ID: <20090520215029.692678819@fluff.org.uk> (raw)

Add support for the ADC controller on the S3C
series of processors to drivers/hwmon for use with
hardware monitoring systems.

Signed-off-by: Ben Dooks <ben@simtec.co.uk>

Index: linux.git/drivers/hwmon/Kconfig
=================================--- linux.git.orig/drivers/hwmon/Kconfig	2009-05-20 22:02:06.000000000 +0100
+++ linux.git/drivers/hwmon/Kconfig	2009-05-20 22:02:43.000000000 +0100
@@ -702,6 +702,16 @@ config SENSORS_SHT15
 	  This driver can also be built as a module.  If so, the module
 	  will be called sht15.
 
+config SENSORS_S3C_ADC
+	tristate "S3C24XX/S3C64XX Inbuilt ADC"
+	depends on HWMON && (ARCH_S3C2410 || ARCH_S3C64XX)
+	help
+	  If you say yes here you get support for the on-board ADCs of
+	  the Samsung S3C24XX or S3C64XX series of SoC
+
+	  This driver can also be built as a module. If so, the module
+	  will be called s3c-adc.
+
 config SENSORS_SIS5595
 	tristate "Silicon Integrated Systems Corp. SiS5595"
 	depends on PCI
Index: linux.git/drivers/hwmon/Makefile
=================================--- linux.git.orig/drivers/hwmon/Makefile	2009-05-20 22:02:06.000000000 +0100
+++ linux.git/drivers/hwmon/Makefile	2009-05-20 22:02:43.000000000 +0100
@@ -76,6 +76,7 @@ obj-$(CONFIG_SENSORS_MAX6650)	+= max6650
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_SENSORS_S3C_ADC)	+= s3c-adc.o
 obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o
 obj-$(CONFIG_SENSORS_SIS5595)	+= sis5595.o
 obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
Index: linux.git/drivers/hwmon/s3c-adc.c
=================================--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux.git/drivers/hwmon/s3c-adc.c	2009-05-20 22:20:32.000000000 +0100
@@ -0,0 +1,371 @@
+/* linux/drivers/hwmon/s3c-adc.c
+ *
+ * Copyright (C) 2005, 2008, 2009 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX/S3C64XX ADC hwmon support
+ *
+ * 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.
+ *
+ * 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
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <plat/adc.h>
+#include <plat/hwmon.h>
+
+/**
+ * struct s3c_hwmon - ADC hwmon client information
+ * @lock: Access lock for conversion.
+ * @wait: Wait queue for conversions to complete.
+ * @client: The client we registered with the S3C ADC core.
+ * @dev: The platform device we bound to.
+ * @hwmon_dev: The hwmon device we created.
+ * @in_attr: The device attributes we created.
+*/
+struct s3c_hwmon {
+	struct semaphore	lock;
+	wait_queue_head_t	wait;
+	int			val;
+
+	struct s3c_hwmon_client	*client;
+	struct platform_device	*dev;
+	struct device		*hwmon_dev;
+
+	struct sensor_device_attribute *in_attr[8];
+};
+
+static inline struct s3c_hwmon *dev_to_ourhwmon(struct platform_device *dev)
+{
+	return (struct s3c_hwmon *)platform_get_drvdata(dev);
+}
+
+static struct s3c_hwmon *done_adc;
+
+/**
+ * s3c_hwmon_adcdone - ADC core callback
+ * @value: The value that we got from the ADC core
+ * @ignore: Only used for the touchscreen client.
+ * @left: The number of conversions left (not used here).
+ *
+ * This is called when the ADC has finished its conversion to
+ * inform us of the result.
+ */
+static void s3c_hwmon_adcdone(unsigned value, unsigned ignore, unsigned *left)
+{
+	struct s3c_hwmon *adc = done_adc;
+
+	hwmon->val = value;
+	wake_up(&hwmon->wait);
+}
+
+/**
+ * s3c_hwmon_read_ch - read a value from a given adc channel.
+ * @hwmon: Our state.
+ * @channel: The channel we're reading from.
+ *
+ * Read a value from the @channel with the proper locking and sleep until
+ * either the read completes or we timeout awaiting the ADC core to get
+ * back to us.
+ */
+static int s3c_hwmon_read_ch(struct s3c_hwmon *hwmon, int channel)
+{
+	unsigned long timeout;
+	int ret;
+
+	ret = down_interruptible(&hwmon->lock);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(&hwmon->dev->dev, "reading channel %d\n", channel);
+
+	hwmon->val = -1;
+	done_adc = adc;
+
+	ret = s3c_adc_start(hwmon->client, channel, 1);
+	if (ret < 0)
+		goto err;
+
+	timeout = wait_event_timeout(hwmon->wait, hwmon->val >= 0, HZ / 2);
+	ret = (timeout = 0) ? -ETIMEDOUT : hwmon->val;
+
+ err:
+	up(&hwmon->lock);
+	return ret;
+}
+
+/**
+ * s3c_hwmon_show_raw - show a conversion from the raw channel number.
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * This show deals with the raw attribute, registered for each possible
+ * ADC channel. This does a conversion and returns the raw (un-scaled)
+ * value returned from the hardware.
+ */
+static ssize_t s3c_hwmon_show_raw(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct s3c_hwmon *adc = dev_to_ourhwmon(to_platform_device(dev));
+	int ret, nr;
+
+	nr = attr->attr.name[3] - '0';
+	ret = s3c_hwmon_read_ch(adc, nr);
+
+	return  (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+#define DEF_ADC_ATTR(x)	\
+	static DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, 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 *s3c_hwmon_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,
+};
+
+/**
+ * s3c_hwmon_ch_show - show value of a given channel
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * Read a value from the ADC and scale it before returning it to the
+ * caller. The scale factor is gained from the channel configuration
+ * passed via the platform data when the device was registered.
+ */
+static ssize_t s3c_hwmon_ch_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
+	struct s3c_hwmon_pdata *pdata = dev->platform_data;
+	struct s3c_hwmon *adc = dev_to_ourhwmon(to_platform_device(dev));
+	struct s3c_hwmon_chcfg *cfg;
+
+	int ret;
+
+	cfg = pdata->in[sen_attr->index];
+	if (!cfg)
+		return -EINVAL;
+
+	ret = s3c_hwmon_read_ch(adc,sen_attr->index);
+	if (ret < 0)
+		return ret;
+
+	ret *= cfg->mult;
+	ret /= cfg->div;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+#define MAX_LABEL (16)
+
+/**
+ * s3c_hwmon_create_attr - create hwmon attribute for given channel.
+ * @hwmon: Our hwmon instance.
+ * @pdata: The platform data provided by the device.
+ * @channel: The ADC channel number to process.
+ *
+ * Create the scaled attribute for use with hwmon from the specified
+ * platform data in @pdata. The sysfs entry is handled by the routine
+ * s3c_hwmon_ch_show().
+ *
+ * The attribute name is taken from the configuration data if present
+ * otherwise the name is taken by concatenating in_ with the channel
+ * number.
+ */
+static int s3c_hwmon_create_attr(struct s3c_hwmon *hwmon,
+				 struct s3c_hwmon_pdata *pdata,
+				 int channel)
+{
+	struct s3c_hwmon_chcfg *cfg = pdata->in[channel];
+	struct sensor_device_attribute *attr;
+	const char *name;
+
+	if (!cfg)
+		return 0;
+
+	attr = kzalloc(sizeof(*attr) + MAX_LABEL, GFP_KERNEL);
+	if (attr = NULL)
+		return -ENOMEM;
+
+	if (cfg->name)
+		name = cfg->name;
+	else {
+		name = (char *)(attr+1);
+		snprintf((char *)(attr+1), MAX_LABEL, "in_%d", channel);
+	}
+
+	attr->dev_attr.attr.name  = name;
+	attr->dev_attr.attr.mode  = S_IRUGO;
+	attr->dev_attr.attr.owner = THIS_MODULE;
+	attr->dev_attr.show = s3c_hwmon_ch_show;
+
+	attr->index = channel;
+	hwmon->in_attr[channel] = attr;
+
+	return device_create_file(&hwmon->dev->dev, &attr->dev_attr);
+}
+
+/**
+ * s3c_hwmon_probe - device probe entry.
+ * @dev: The device being probed.
+*/
+static int s3c_hwmon_probe(struct platform_device *dev)
+{
+	struct s3c_hwmon_pdata *pdata = dev->dev.platform_data;
+	struct s3c_hwmon *hwmon;
+	int ret = 0;
+	int i;
+
+	adc = kzalloc(sizeof(struct s3c_hwmon), GFP_KERNEL);
+	if (adc = NULL) {
+		dev_err(&dev->dev, "no memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(dev, adc);
+	hwmon->dev = dev;
+
+	/* only enable the clock when we are actually using the adc */
+
+	init_waitqueue_head(&hwmon->wait);
+	init_MUTEX(&hwmon->lock);
+
+	/* Register with the core ADC driver. */
+
+	hwmon->client = s3c_adc_register(dev, NULL, s3c_hwmon_adcdone, 0);
+	if (IS_ERR(hwmon->client)) {
+		dev_err(&dev->dev, "cannot register adc\n");
+		ret = PTR_ERR(hwmon->client);
+		goto err_mem;
+	}
+
+	/* add attributes for our adc devices. */
+
+	for (i = 0; i < ARRAY_SIZE(s3c_hwmon_attrs); i++) {
+		ret = device_create_file(&dev->dev, s3c_hwmon_attrs[i]);
+		if (ret) {
+			dev_err(&dev->dev, "error creating channel %d\n", i);
+
+			for (i--; i >= 0; i--)
+				device_remove_file(&dev->dev, s3c_hwmon_attrs[i]);
+
+			goto err_registered;
+		}
+	}
+
+	/* register with the hwmon core */
+
+	hwmon->hwmon_dev = hwmon_device_register(&dev->dev);
+	if (IS_ERR(hwmon->hwmon_dev)) {
+		dev_err(&dev->dev, "error registering with hwmon\n");
+		ret = PTR_ERR(hwmon->hwmon_dev);
+		goto err_attribute;
+	}
+
+	if (pdata) {
+		for (i = 0; i < ARRAY_SIZE(pdata->in); i++)
+			s3c_hwmon_create_attr(adc, pdata, i);
+	}
+
+	return 0;
+
+ err_attribute:
+	for (i = 0; i < ARRAY_SIZE(s3c_hwmon_attrs); i++)
+		device_remove_file(&dev->dev, s3c_hwmon_attrs[i]);
+
+ err_registered:
+	s3c_adc_release(hwmon->client);
+
+ err_mem:
+	kfree(adc);
+	return ret;
+}
+
+static int __devexit s3c_hwmon_remove(struct platform_device *dev)
+{
+	struct s3c_hwmon *hwmon = dev_to_ourhwmon(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s3c_hwmon_attrs); i++)
+		device_remove_file(&dev->dev, s3c_hwmon_attrs[i]);
+
+	for (i = 0; i < ARRAY_SIZE(hwmon->in_attr); i++) {
+		if (!hwmon->in_attr[i])
+			continue;
+
+		device_remove_file(&dev->dev, &hwmon->in_attr[i]->dev_attr);
+		kfree(hwmon->in_attr[i]);
+	}
+
+	hwmon_device_unregister(hwmon->hwmon_dev);
+	s3c_adc_release(hwmon->client);
+
+	return 0;
+}
+
+static struct platform_driver s3c_hwmon_driver = {
+	.driver	= {
+		.name		= "s3c-hwmon",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= s3c_hwmon_probe,
+	.remove		= __devexit_p(s3c_hwmon_remove),
+};
+
+static int __init s3c_adc_init(void)
+{
+	return platform_driver_register(&s3c_hwmon_driver);
+}
+
+static void __exit s3c_adc_exit(void)
+{
+	platform_driver_unregister(&s3c_hwmon_driver);
+}
+
+module_init(s3c_adc_init);
+module_exit(s3c_adc_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C ADC HWMon driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-hwmon");
Index: linux.git/arch/arm/plat-s3c/include/plat/hwmon.h
=================================--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux.git/arch/arm/plat-s3c/include/plat/hwmon.h	2009-05-20 22:02:43.000000000 +0100
@@ -0,0 +1,43 @@
+/* linux/arch/arm/plat-s3c/include/plat/hwmon.h
+ *
+ * Copyright 2005 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	http://armlinux.simtec.co.uk/
+ *
+ * S3C - 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.
+*/
+
+#ifndef __ASM_ARCH_ADC_HWMON_H
+#define __ASM_ARCH_ADC_HWMON_H __FILE__
+
+/**
+ * s3c_hwmon_chcfg - channel configuration
+ * @name: The name to give this channel.
+ * @mult: Multiply the ADC value read by this.
+ * @div: Divide the value from the ADC by this.
+ *
+ * The value read from the ADC is converted to a value that
+ * hwmon expects (mV) by result = (value_read * @mult) / @div.
+ */
+struct s3c_hwmon_chcfg {
+	const char	*name;
+	unsigned int	min;
+	unsigned int	max;
+	unsigned int	mult;
+	unsigned int	div;
+};
+
+/**
+ * s3c_hwmon_pdata - HWMON platform data
+ * @in: One configuration for each possible channel used.
+ */
+struct s3c_hwmon_pdata {
+	struct s3c_hwmon_chcfg	*in[8];
+};
+
+#endif /* __ASM_ARCH_ADC_HWMON_H */
+

-- 
Ben (ben@fluff.org, http://www.fluff.org/)

  'a smiley only costs 4 bytes'

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

             reply	other threads:[~2009-05-20 21:50 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-05-20 21:50 Ben Dooks [this message]
2009-05-25  9:39 ` [lm-sensors] HWMON: S3C24XX series ADC driver Ramax Lo
2009-05-26  8:01 ` Ben Dooks
2009-05-26  8:07 ` Ben Dooks
2009-05-26 15:08 ` Hector Oron
2009-05-26 19:20 ` Hector Oron
2009-05-26 21:25 ` Ben Dooks
2009-05-27 20:01 ` Jean Delvare
2009-05-28 11:22 ` Ben Dooks
2009-05-28 11:47 ` Jean Delvare
2009-05-28 13:16 ` Ben Dooks
2009-05-28 15:21 ` Ben Dooks
2009-05-28 15:55 ` Jean Delvare
2009-05-28 20:42 ` Ben Dooks
2009-05-30 12:41 ` Jean Delvare
2009-05-30 13:47 ` Ben Dooks
2009-05-30 14:53 ` Jean Delvare
2009-05-30 15:43 ` Ben Dooks
2009-06-03 10:14 ` Hector Oron
2009-06-08 11:35 ` Ben Dooks
2009-06-10  7:59 ` Jean Delvare
2009-06-12  9:04 ` Ben Dooks
2009-06-12  9:15 ` Jean Delvare

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=20090520215029.692678819@fluff.org.uk \
    --to=ben@simtec.co.uk \
    --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.