[lm-sensors] Intel Moorestown Analog Accelerometer driver

Ramesh Agarwal notagarwal at gmail.com
Thu Aug 13 10:47:31 CEST 2009


Hello,

Attached is the patch that adds support for the Analog accelerometer
on Intel's Moorestown platform. This has a dependency on the  Langwell
driver that was submitted to the kernel mailing list but is still not
in the mainstream. The patch can be found at
http://lkml.org/lkml/2009/7/20/396. Sending this out here to get any
early review comments on the code.

Thanks,
Ramesh
------------------------------------------------------------------------------------
>From 3bd9a4332cfd7ec1471cc8beb9495fb2fe7ff84b Mon Sep 17 00:00:00 2001
From: Ramesh Agarwal <ramesh.agarwal at intel.com>
Date: Mon, 10 Aug 2009 11:09:43 +0530
Subject: [PATCH] Intel Moorestown Analog Accelerometer Driver

This patch adds the driver support for the Analog accelerometer
on the Moorestown platform. The acceleromater output is hooked to the
ADC lines on the PMIC and the driver uses the Langwell IPC to communicate
with the PMIC to get the output from the accelerometer.

Signed-off-by: Ramesh Agarwal <ramesh.agarwal at intel.com>
---
 Documentation/hwmon/mrst_analog_accel |   29 +++
 drivers/hwmon/Kconfig                 |    7 +
 drivers/hwmon/Makefile                |    1 +
 drivers/hwmon/mrst_analog_accel.c     |  331 +++++++++++++++++++++++++++++++++
 4 files changed, 368 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/hwmon/mrst_analog_accel
 create mode 100644 drivers/hwmon/mrst_analog_accel.c

diff --git a/Documentation/hwmon/mrst_analog_accel
b/Documentation/hwmon/mrst_analog_accel
new file mode 100644
index 0000000..a98a861
--- /dev/null
+++ b/Documentation/hwmon/mrst_analog_accel
@@ -0,0 +1,29 @@
+Kernel driver mrst_analog_accel
+===============================
+
+Supported chips:
+  * PMIC accelerometer for Langwell
+    Prefix: 'mrst_analog_accel'
+
+Author: Ramesh Agarwal <ramesh.agarwal at intel.com>
+
+Description
+-----------
+
+This driver provides support for the analog accelerometer connected
+to the Moorestown Platform. The accelerometer is a Freescale MMA7341L
+analog device whose Xout, Yout and Zout are connected to the ADC on the
+PMIC of the Moorestown platform.
+
+The driver registers as a platform device driver and presents a single
+attribute 'position'. This attribute contains the uncalibrated values
+for the x, y and z co-ordinates as read from the ADC. The driver uses
+the Langwell IPC driver (LANGWELL_IPC)to read the values from the ADC.
+The accelerometer data is readable at /sys/devices/platform/mrst_analog_accel
+
+The accelerometer operates in the polling mode. This is to say that sampling
+by the driver will be done only if a user application has requested for the
+values.  The driver does not  register as an input device, and hence input
+calibration and fixing the sampling rate is left to the user level
+framework/application. The sysfs interface only provides the decimal values
+of the voltage read from the ADC where each bit is equivalent to 2mV.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2d50166..8993e55 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -652,6 +652,13 @@ config SENSORS_MAX6650
 	  This driver can also be built as a module.  If so, the module
 	  will be called max6650.

+config SENSORS_MRST_ANALOG_ACCEL
+	tristate "Moorestown Analog Accelerometer"
+	depends on LANGWELL_IPC
+	help
+	  If you say yes here you get support for the Analog Accelerometer
+	  Device x y Z data can be accessed via sysfs.
+
 config SENSORS_PC87360
 	tristate "National Semiconductor PC87360 family"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b793dce..2bc0bbc 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_SENSORS_LTC4245)	+= ltc4245.o
 obj-$(CONFIG_SENSORS_MAX1111)	+= max1111.o
 obj-$(CONFIG_SENSORS_MAX1619)	+= max1619.o
 obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
+obj-$(CONFIG_SENSORS_MRST_ANALOG_ACCEL)	+= mrst_analog_accel.o
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
diff --git a/drivers/hwmon/mrst_analog_accel.c
b/drivers/hwmon/mrst_analog_accel.c
new file mode 100644
index 0000000..366c086
--- /dev/null
+++ b/drivers/hwmon/mrst_analog_accel.c
@@ -0,0 +1,331 @@
+/*
+ * mrst_analog_accel.c - Intel analog accelerometer driver for Moorestown
+ *
+ * Copyright (C) 2009 Intel Corp
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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.
+ *
+ * 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/platform_device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <asm/mrst_ipcdefs.h>
+
+MODULE_AUTHOR("Ramesh Agarwal");
+MODULE_DESCRIPTION("Intel Moorestown Analog Accelerometer Driver");
+MODULE_LICENSE("GPL v2");
+
+/* PMIC ADC INTERRUPT REGISTERS */
+#define PMIC_ADC_ACC_REG_ADCINT		0x5F	/*ADC interrupt register */
+#define PMIC_ADC_ACC_REG_MADCINT	0x60	/*ADC interrupt mask register */
+
+/* PMIC ADC CONTROL REGISTERS */
+#define PMIC_ADC_ACC_REG_ADCCNTL1	0x61	/*ADC control register */
+#define PMIC_ADC_ACC_REG_ADCCNTL2	0x62	/*ADC gain regs channel 10-17 */
+#define PMIC_ADC_ACC_REG_ADCCNTL3	0x63	/*ADC gain regs channel 18-21 */
+
+/* PMIC Data Register base */
+#define PMIC_ADC_DATA_REG_BASE		0x64
+
+/* PMIC Channel Mapping Register base */
+#define PMIC_ADC_MAPPING_BASE		0xA4
+
+/* Register offsets for XYZ co-ordinates */
+#define PMIC_ADC_X_OFFSET		0x0
+#define PMIC_ADC_Y_OFFSET		0x2
+#define PMIC_ADC_Z_OFFSET		0x4
+
+/* Bit mask to check if ACD is enabled */
+#define PMIC_ADC_ENABLED_BIT_MASK	0x80
+
+/* Number of PMIC sample registers */
+#define PMIC_ADC_REG_MAX	32	/* Max no of available channel */
+
+#define PMIC_ADC_REG_HIGH(index, offset)	(PMIC_ADC_DATA_REG_BASE \
+						+ (index) * 2 + offset)
+#define PMIC_ADC_REG_LOW(index, offset)		(PMIC_ADC_DATA_REG_BASE \
+						+ (index) * 2 + offset + 1)
+
+/* Number of registers to read at a time */
+#define REG_READ_PER_IPC	4	/* Read 4 at a time although the */
+					/* IPC will support max 5 */
+
+#define END_OF_CHANNEL_VALUE	0x1F	/* Used to indicate the last */
+					/* channel being used */
+
+/* PMIC ADC channels for Accelero Meter */
+#define PMIC_ADC_ACC_ADC_ACC_CH14  0xE
+#define PMIC_ADC_ACC_ADC_ACC_CH15  0xF
+#define PMIC_ADC_ACC_ADC_ACC_CH16  0x10
+
+static unsigned int mrst_analog_reg_idx;
+
+static unsigned int analog_accel_read(int offset)
+{
+	unsigned int ret_val;
+	struct mrst_pmic_reg_data ipc_data;
+
+	ipc_data.ioc = 0;	/* No need to generate MSI */
+	ipc_data.num_entries = 2;
+	ipc_data.pmic_reg_data[0].register_address =
+		PMIC_ADC_REG_HIGH(mrst_analog_reg_idx,
+				offset); /* Higher 8 bits */
+	ipc_data.pmic_reg_data[1].register_address =
+		PMIC_ADC_REG_LOW(mrst_analog_reg_idx,
+				offset); /* Lower 3 bits */
+	if (mrst_pmic_ioread(&ipc_data) != 0) {
+		printk(KERN_ERR
+		"mrst_analog_accel:PMIC reg read using IPC failed\n");
+		return -1;
+	}
+	ret_val = ipc_data.pmic_reg_data[0].value << 3;
+	ret_val = ret_val | (ipc_data.pmic_reg_data[1].value & 0x07);
+	return ret_val;
+}
+
+/* Use IPC to read the value of the register and display */
+static ssize_t mrst_analog_accel_position_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d %d %d\n",
+				analog_accel_read(PMIC_ADC_X_OFFSET),
+				analog_accel_read(PMIC_ADC_Y_OFFSET),
+				analog_accel_read(PMIC_ADC_Z_OFFSET));
+}
+
+
+static DEVICE_ATTR(position, S_IRUGO,
+			mrst_analog_accel_position_show, NULL);
+
+static struct attribute *mid_att_acc[] = {
+	&dev_attr_position.attr,
+	NULL
+};
+
+static struct attribute_group m_analog_gr = {
+	.name = "mrst_analog_accel",
+	.attrs = mid_att_acc
+};
+
+static int mrst_analog_accel_initialize(void)
+{
+	struct mrst_pmic_mod_reg_data ipc_mod_data;
+	struct mrst_pmic_reg_data ipc_data;
+	u8 retval = 0;
+	u8 mad_cntrl = 0;	/* MADCINT register value */
+	u8 adc_cntrl1 = 0;	/* ADCCNTL2 register value */
+	int i, j;
+
+	/* Initialize the register index to use to be zero */
+	mrst_analog_reg_idx = 0;
+
+	/* check if the ADC is enabled or not
+	 * Read ADCCNTL1 registers */
+	ipc_data.ioc = 0;	/* No need to generate MSI */
+	ipc_data.num_entries = 1;
+	ipc_data.pmic_reg_data[0].register_address =
+		PMIC_ADC_ACC_REG_ADCCNTL1;
+	ipc_data.pmic_reg_data[0].value = 0;
+
+	retval = mrst_pmic_ioread(&ipc_data);
+	if (retval != 0) {
+		printk(KERN_ERR
+			"mrst_analog_accel:PMIC register read failed\n");
+		return retval;
+	}
+
+	adc_cntrl1 = ipc_data.pmic_reg_data[0].value;
+
+	if (adc_cntrl1 & PMIC_ADC_ENABLED_BIT_MASK) {
+		/* If the ADC is enabled find the set of registers to use
+		** Loop through the channel mapping register to find out the
+		** first free one
+		*/
+		for (i = 0;
+			(i < PMIC_ADC_REG_MAX) && (mrst_analog_reg_idx == 0);
+			i += REG_READ_PER_IPC) {
+
+			ipc_data.num_entries = REG_READ_PER_IPC;
+			ipc_data.ioc = 0;	/* No need to generate MSI */
+
+			/* Reading 4 regs at a time instead of reading each
+			 * reg one by one since IPC is an expensive operation
+			 */
+			for (j = 0; j < REG_READ_PER_IPC; j++) {
+				ipc_data.pmic_reg_data[j].register_address =
+					PMIC_ADC_MAPPING_BASE + i + j;
+				ipc_data.pmic_reg_data[j].value = 0;
+			}
+			retval = mrst_pmic_ioread(&ipc_data);
+			if (retval != 0) {
+				printk(KERN_ERR
+				"mrst_analog_accel:PMIC regs read failed\n");
+				return retval;
+			}
+			for (j = 0; j < REG_READ_PER_IPC; j++) {
+				if (ipc_data.pmic_reg_data[j].value
+					== END_OF_CHANNEL_VALUE) {
+					mrst_analog_reg_idx = i + j;
+					break;
+				}
+			}
+		}
+	}
+	/* Check to see if there are enough registers to map the channel */
+	if ((mrst_analog_reg_idx + 3) >= PMIC_ADC_REG_MAX) {
+		printk(KERN_ERR
+		"mrst_analog_accel:Not enough regs to map the channels\n");
+		return -1;
+	}
+
+	/* Update the mapping registers for the accelerometer*/
+	ipc_data.num_entries = 4;
+	ipc_data.ioc = 0;	/* No need to generate MSI */
+	ipc_data.pmic_reg_data[0].register_address =
+			PMIC_ADC_MAPPING_BASE + mrst_analog_reg_idx;
+	ipc_data.pmic_reg_data[0].value = PMIC_ADC_ACC_ADC_ACC_CH14;
+
+	ipc_data.pmic_reg_data[1].register_address =
+			PMIC_ADC_MAPPING_BASE + mrst_analog_reg_idx + 1;
+	ipc_data.pmic_reg_data[1].value = PMIC_ADC_ACC_ADC_ACC_CH15;
+
+	ipc_data.pmic_reg_data[2].register_address =
+			PMIC_ADC_MAPPING_BASE + mrst_analog_reg_idx + 2;
+	ipc_data.pmic_reg_data[2].value = PMIC_ADC_ACC_ADC_ACC_CH16;
+
+	ipc_data.pmic_reg_data[3].register_address =
+			PMIC_ADC_MAPPING_BASE + mrst_analog_reg_idx + 3 ;
+	ipc_data.pmic_reg_data[3].value = END_OF_CHANNEL_VALUE;
+
+	retval = mrst_pmic_iowrite(&ipc_data);
+	if (retval != 0) {
+		printk(KERN_ERR
+			"mrst_analog_accel:PMIC reg write failed\n");
+		return retval;
+	}
+
+	/* If the ADC was not enabled, enable it now */
+	if (!(adc_cntrl1 & PMIC_ADC_ENABLED_BIT_MASK)) {
+		/* Mask the round robin completion interrupt */
+		ipc_mod_data.ioc = 0;	/* No need to generate MSI */
+		ipc_mod_data.num_entries = 1;
+		mad_cntrl = 0x01;
+		ipc_mod_data.pmic_mod_reg_data[0].register_address =
+			PMIC_ADC_ACC_REG_MADCINT;
+		ipc_mod_data.pmic_mod_reg_data[0].value = mad_cntrl;
+		ipc_mod_data.pmic_mod_reg_data[0].bit_map = 0x01;
+
+		retval = mrst_pmic_ioread_modify(&ipc_mod_data);
+		if (retval != 0) {
+			printk(KERN_ERR
+			"mrst_analog_accel:PMIC reg modify failed\n");
+			return retval;
+		}
+
+		adc_cntrl1 = 0xc6;	/*27ms delay,start round robin,
+					   enable full power */
+		ipc_data.ioc = 0;	/* No need to generate MSI */
+		ipc_data.num_entries = 1;
+		ipc_data.pmic_reg_data[0].register_address =
+			PMIC_ADC_ACC_REG_ADCCNTL1;
+		ipc_data.pmic_reg_data[0].value = adc_cntrl1;
+		retval = mrst_pmic_iowrite(&ipc_data);
+		if (retval != 0)
+			return retval;
+	}
+	return retval;
+}
+
+static struct platform_device *mrst_analog_accel_pdev;
+static struct device *mrst_analog_accel_hwmon;
+
+static int mrst_analog_accel_unregister(void)
+{
+
+	hwmon_device_unregister(mrst_analog_accel_hwmon);
+	sysfs_remove_group(&mrst_analog_accel_pdev->dev.kobj, &m_analog_gr);
+	platform_device_unregister(mrst_analog_accel_pdev);
+	printk(KERN_INFO "mrst_analog_accel:Unregistered the device\n");
+	return 0;
+}
+
+
+static int __init mrst_analog_accel_module_init(void)
+{
+	int retval = 0;
+
+	mrst_analog_accel_pdev =
+		platform_device_register_simple("mrst_analog_accel",
+			0, NULL, 0);
+	if (IS_ERR(mrst_analog_accel_pdev)) {
+		retval = PTR_ERR(mrst_analog_accel_pdev);
+		printk(KERN_ERR
+		"mrst_analog_accel:Registration with the platform failed\n");
+		return retval;
+	}
+
+	retval = mrst_analog_accel_initialize();
+	if (retval == 0) {
+		retval = sysfs_create_group(&mrst_analog_accel_pdev->dev.kobj,
+			&m_analog_gr);
+		if (retval) {
+			printk(KERN_ERR
+			"mrst_analog_accel:device_create_file 1 failed\n");
+			goto accelero_reg_failed;
+		}
+		mrst_analog_accel_hwmon =
+			hwmon_device_register(&mrst_analog_accel_pdev->dev);
+		if (IS_ERR(mrst_analog_accel_hwmon)) {
+			retval = PTR_ERR(mrst_analog_accel_hwmon);
+			mrst_analog_accel_hwmon = NULL;
+			sysfs_remove_group(&mrst_analog_accel_pdev->dev.kobj,
+					&m_analog_gr);
+			printk(KERN_ERR
+			"mrst_analog_accel:Registration with hwmon failed\n");
+			goto accelero_reg_failed;
+		}
+	} else {
+		printk(KERN_ERR
+		"mrst_analog_accel:Initialization failed: %d\n", retval);
+		goto accelero_reg_failed;
+	}
+
+	printk(KERN_INFO
+		"mrst_analog_accel:Registered device with the platform\n");
+	return retval;
+
+accelero_reg_failed:
+	platform_device_unregister(mrst_analog_accel_pdev);
+	return retval;
+}
+
+static void __exit mrst_analog_accel_module_exit(void)
+{
+
+	mrst_analog_accel_unregister();
+}
+
+module_init(mrst_analog_accel_module_init);
+module_exit(mrst_analog_accel_module_exit);
-- 
1.5.6




More information about the lm-sensors mailing list