Skip to contentSkip to conversation

Apple Motion Sensor driver

RFC In review Michael Hanselmann 9 participants 19 messages Jul 2, 2006 – Jul 4, 2006 Hardware monitoring

lore
Share & tools
lore
#0 lore raw

RFC 1/1 Apple Motion Sensor driver

Hello

Below you find the latest revision of my AMS driver. AMS stands for Apple Motion Sensor and it's included in the 2005 revisions of Apple iBooks and PowerBooks. The driver implements the PMU and I2C variants. MacBooks have another variant, which is not handled by this driver, mainly because it's totally different and I don't have access to a MacBook.

Some of the code is based on the I2C ams driver from Stelian Pop, whom I'd like to thank for his work.

HD parking is marked BROKEN as of now, because it'll need changes to the block, IDE and SCSI layers. The infrastructure works, tough (tested with my own HD parking code). That's why I left it in there.

I want this driver to be included in -mm as soon as possible, to get test feedback and to get it included in 2.6.19 (or maybe 2.6.18? Who knows. ;)). Thus I'd like to get your comments, suggestions, etc. on it.

Thanks for your feedback,
Michael

---

drivers/hwmon/ams-core.c +264 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/ams-core.c linux-2.6.17-git20/drivers/hwmon/ams-core.c
@@ -0,0 +1,264 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian at popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel at hansmi.ch)
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/pmac_pfunc.h>
+
+#include "ams.h"
+
+/* There is only one motion sensor per machine */
+struct ams ams;
+
+static unsigned int verbose;
+module_param(verbose, bool, 0);
+MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
+
+/* Call with lock held! */
+void ams_sensors(s8 *x, s8 *y, s8 *z)
+{
+	s8 tmpx, tmpy, tmpz;
+	u32 orient;
+
+	orient = ams.vflag? ams.orient1 : ams.orient2;
+
+	if (orient & 0x80)
+		/* X and Y swapped */
+		ams.get_xyz(&tmpy, &tmpx, &tmpz);
+	else
+		ams.get_xyz(&tmpx, &tmpy, &tmpz);
+
+	if (orient & 0x04)
+		tmpz = ~tmpz;
+	if (orient & 0x02)
+		tmpy = ~tmpy;
+	if (orient & 0x01)
+		tmpx = ~tmpx;
+
+	if (x)
+		*x = tmpx;
+	if (y)
+		*y = tmpy;
+	if (z)
+		*z = tmpz;
+}
+
+static ssize_t ams_show_xyz(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	s8 x, y, z;
+
+	mutex_lock(&ams.lock);
+	ams_sensors(&x, &y, &z);
+	mutex_unlock(&ams.lock);
+
+	return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z);
+}
+
+static DEVICE_ATTR(xyz, S_IRUGO, ams_show_xyz, NULL);
+
+static void ams_handle_irq(void *data)
+{
+	enum ams_irq irq = *((enum ams_irq *)data);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ams.irq_lock, flags);
+
+	ams.worker_irqs |= irq;
+	schedule_work(&ams.worker);
+
+	spin_unlock_irqrestore(&ams.irq_lock, flags);
+}
+
+static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
+static struct pmf_irq_client ams_freefall_client = {
+	.owner = THIS_MODULE,
+	.handler = ams_handle_irq,
+	.data = &ams_freefall_irq_data,
+};
+
+static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
+static struct pmf_irq_client ams_shock_client = {
+	.owner = THIS_MODULE,
+	.handler = ams_handle_irq,
+	.data = &ams_shock_irq_data,
+};
+
+static void ams_worker(void *data)
+{
+	mutex_lock(&ams.lock);
+
+	if (ams.has_device) {
+		int do_park = 0;
+
+		if (ams.worker_irqs & AMS_IRQ_FREEFALL) {
+			if (verbose)
+				printk(KERN_INFO "ams: freefall detected!\n");
+
+			ams.worker_irqs &= ~AMS_IRQ_FREEFALL;
+			ams.clear_irq(AMS_IRQ_FREEFALL);
+
+			do_park = 1;
+		}
+
+		if (ams.worker_irqs & AMS_IRQ_SHOCK) {
+			if (verbose)
+				printk(KERN_INFO "ams: shock detected!\n");
+
+			ams.worker_irqs &= ~AMS_IRQ_SHOCK;
+			ams.clear_irq(AMS_IRQ_SHOCK);
+
+			do_park = 1;
+		}
+
+		if (do_park)
+			ams_hdpark();
+	}
+
+	mutex_unlock(&ams.lock);
+}
+
+/* Call with lock held! */
+int ams_sensor_attach(void)
+{
+	int result;
+	u32 *prop;
+
+	/* Get orientation */
+	prop = (u32*)get_property(ams.of_node, "orientation", NULL);
+	if (!prop)
+		return -ENODEV;
+	ams.orient1 = *prop;
+	ams.orient2 = *(prop + 1);
+
+	/* Register freefall interrupt handler */
+	result = pmf_register_irq_client(ams.of_node,
+			"accel-int-1",
+			&ams_freefall_client);
+	if (result < 0)
+		return -ENODEV;
+
+	/* Reset saved irqs */
+	ams.worker_irqs = 0;
+
+	/* Register shock interrupt handler */
+	result = pmf_register_irq_client(ams.of_node,
+			"accel-int-2",
+			&ams_shock_client);
+	if (result < 0) {
+		pmf_unregister_irq_client(&ams_freefall_client);
+		return -ENODEV;
+	}
+
+	/* Create device */
+	ams.of_dev = of_platform_device_create(ams.of_node, "ams", NULL);
+	if (!ams.of_dev) {
+		pmf_unregister_irq_client(&ams_shock_client);
+		pmf_unregister_irq_client(&ams_freefall_client);
+		return -ENODEV;
+	}
+
+	/* Create attributes */
+	device_create_file(&ams.of_dev->dev, &dev_attr_xyz);
+
+	ams.vflag = !!(ams.get_vendor() & 0x10);
+
+	/* Init hdpark stuff */
+	ams_hdpark_init();
+
+	/* Init mouse device */
+	ams_mouse_init();
+
+	return 0;
+}
+
+int __init ams_init(void)
+{
+	struct device_node *np;
+
+	spin_lock_init(&ams.irq_lock);
+	mutex_init(&ams.lock);
+	INIT_WORK(&ams.worker, ams_worker, NULL);
+
+#ifdef CONFIG_SENSORS_AMS_I2C
+	np = of_find_node_by_name(NULL, "accelerometer");
+	if (np && device_is_compatible(np, "AAPL,accelerometer_1"))
+		/* Found I2C motion sensor */
+		return ams_i2c_init(np);
+#endif
+
+#ifdef CONFIG_SENSORS_AMS_PMU
+	np = of_find_node_by_name(NULL, "sms");
+	if (np && device_is_compatible(np, "sms"))
+		/* Found PMU motion sensor */
+		return ams_pmu_init(np);
+#endif
+
+	printk(KERN_ERR "ams: No motion sensor found.\n");
+
+	return -ENODEV;
+}
+
+void ams_exit(void)
+{
+	mutex_lock(&ams.lock);
+
+	if (ams.has_device) {
+		/* Remove mouse device */
+		ams_mouse_exit();
+
+		/* Shut down implementation */
+		ams.exit();
+
+		/* Cancel interrupt worker
+		 *
+		 * We do this after ams.exit(), because an interrupt might
+		 * have arrived before disabling them.
+		 */
+		cancel_delayed_work(&ams.worker);
+		flush_scheduled_work();
+
+		/* Disable hard disk parking */
+		ams_hdpark_exit();
+
+		/* Remove attributes */
+		device_remove_file(&ams.of_dev->dev, &dev_attr_xyz);
+
+		/* Remove device */
+		of_device_unregister(ams.of_dev);
+
+		/* Remove handler */
+		pmf_unregister_irq_client(&ams_shock_client);
+		pmf_unregister_irq_client(&ams_freefall_client);
+	}
+
+	mutex_unlock(&ams.lock);
+}
+
+MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
+MODULE_DESCRIPTION("Apple Motion Sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ams_init);
+module_exit(ams_exit);
drivers/hwmon/ams.h +91 -0

91 lines not on this page · raw message

drivers/hwmon/ams-hdpark.c +72 -0

72 lines not on this page · raw message

drivers/hwmon/ams-i2c.c +284 -0

284 lines not on this page · raw message

drivers/hwmon/ams-mouse.c +146 -0

146 lines not on this page · raw message

drivers/hwmon/ams-pmu.c +202 -0

202 lines not on this page · raw message

drivers/hwmon/Kconfig +36 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Kconfig linux-2.6.17-git20/drivers/hwmon/Kconfig
@@ -507,6 +507,42 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_AMS
+	tristate "Motion sensor driver"
+	default y
+	help
+	  Support for the motion sensor included in PowerBooks.
+
+config SENSORS_AMS_PMU
+	bool "PMU variant"
+	depends on SENSORS_AMS && ADB_PMU
+	default y
+	help
+	  PMU variant of motion sensor, found in late 2005 PowerBooks.
+
+config SENSORS_AMS_I2C
+	bool "I2C variant"
+	depends on SENSORS_AMS && I2C
+	default y
+	help
+	  I2C variant of motion sensor, found in early 2005 PowerBooks and
+	  iBooks.
+
+config SENSORS_AMS_MOUSE
+	bool "Support for mouse mode with motion sensor"
+	depends on SENSORS_AMS && INPUT
+	help
+	  Support for mouse emulation with motion sensor.
+
+config SENSORS_AMS_HDPARK
+	bool "Park hard disk heads on freefall or shock"
+	depends on SENSORS_AMS = y && BROKEN
+	default y
+	help
+	  Park the internal hard disk's heads if a free fall or shock is
+	  detected. This can help to prevent data loss in case of a
+	  head crash, altough nobody can guarantee anything.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
drivers/hwmon/Makefile +7 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Makefile linux-2.6.17-git20/drivers/hwmon/Makefile
@@ -49,6 +49,13 @@ obj-$(CONFIG_SENSORS_VT8231)	+= vt8231.o
 obj-$(CONFIG_SENSORS_W83627EHF)	+= w83627ehf.o
 obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
 
+ams-y					:= ams-core.o
+ams-$(CONFIG_SENSORS_AMS_PMU)		+= ams-pmu.o
+ams-$(CONFIG_SENSORS_AMS_I2C)		+= ams-i2c.o
+ams-$(CONFIG_SENSORS_AMS_MOUSE)		+= ams-mouse.o
+ams-$(CONFIG_SENSORS_AMS_HDPARK)	+= ams-hdpark.o
+obj-$(CONFIG_SENSORS_AMS)		+= ams.o
+
 ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
#1 lore raw

Hi Michael,

drivers/hwmon/ams-core.c line 64

Quoted 8 lines · Michael Hanselmann
drivers/hwmon/ams-core.c +7 -0
+
+	if (x)
+		*x = tmpx;
+	if (y)
+		*y = tmpy;
+	if (z)
+		*z = tmpz;

All callers of ams_sensors use all 3 arguments, why bother with temps?

drivers/hwmon/ams-mouse.c line 65

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+	ams.idev->name = "Apple Motion Sensor";
+	ams.idev->id.bustype = BUS_I2C;

Should it be always BUS_I2C? Maybe it should be BUS_HOST in case of PMU?

drivers/hwmon/ams-mouse.c line 66

Quoted 1 line
drivers/hwmon/ams-mouse.c +1 -0
+	ams.idev->id.vendor = 0;

Please add:

	ams.idev->cdev.dev = &ams.of_dev->dev;

so that the input device has a proper parent in sysfs hierarchy.

Signature · 1 line
Dmitry
#2 lore raw
In reply to Michael Hanselmann
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…

Below you find the latest revision of my AMS driver.

I was about to merge the below, then this comes along. Now what?

From: Stelian Pop <stelian at popies.net>

This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks (the series the PowerBook5,6 falls into, later ones have a slightly different one the driver doesn't handle) are supported. The accelerometer data is readable via sysfs.

This driver also provides an absolute input class device, allowing the laptop to act as a pinball machine-esque joystick.

In the future (once the proper API exist in the block layer) this driver will tell the laptop disk to protect its heads in the event of a catastrophic fall.

Signed-off-by Stelian Pop <stelian at popies.net>
Signed-off-by Johannes Berg <johannes at sipsolutions.net>
Cc Benjamin Herrenschmidt <benh at kernel.crashing.org>
Cc Paul Mackerras <paulus at samba.org>
Cc Dmitry Torokhov <dtor_core at ameritech.net>
Cc Robert Love <rml at novell.com>
Cc Jean Delvare <khali at linux-fr.org>
 drivers/hwmon/Kconfig  |   17 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ams.c    |  544 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 562 insertions(+)
drivers/hwmon/ams.c new file +544 -0
diff -puN /dev/null drivers/hwmon/ams.c
@@ -0,0 +1,544 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian at popies.net)
+ *
+ * Clean room implementation based on the reverse engineered OSX driver by
+ * Johannes Berg <johannes at sipsolutions.net>, documentation available at
+ * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+
+#include <asm/of_device.h>
+#include <asm/pmac_feature.h>
+
+MODULE_DESCRIPTION("Apple Motion Sensor driver");
+MODULE_AUTHOR("Stelian Pop");
+MODULE_LICENSE("GPL");
+
+static unsigned int mouse;
+module_param(mouse, bool, 0);
+MODULE_PARM_DESC(mouse, "enable the input class device on module load");
+
+/* AMS registers */
+#define	AMS_COMMAND	0x00	/* command register */
+#define AMS_STATUS	0x01	/* status register */
+#define AMS_CTRL1	0x02	/* read control 1 (number of values) */
+#define AMS_CTRL2	0x03	/* read control 2 (offset?) */
+#define AMS_CTRL3	0x04	/* read control 3 (size of each value?) */
+#define AMS_DATA1	0x05	/* read data 1 */
+#define AMS_DATA2	0x06	/* read data 2 */
+#define AMS_DATA3	0x07	/* read data 3 */
+#define AMS_DATA4	0x08	/* read data 4 */
+#define AMS_DATAX	0x20	/* data X */
+#define AMS_DATAY	0x21	/* data Y */
+#define AMS_DATAZ	0x22	/* data Z */
+#define AMS_FREEFALL	0x24	/* freefall int control */
+#define AMS_SHOCK	0x25	/* shock int control */
+#define AMS_SENSLOW	0x26	/* sensitivity low limit */
+#define AMS_SENSHIGH	0x27	/* sensitivity high limit */
+#define AMS_CTRLX	0x28	/* control X */
+#define AMS_CTRLY	0x29	/* control Y */
+#define AMS_CTRLZ	0x2A	/* control Z */
+#define AMS_UNKNOWN1	0x2B	/* unknown 1 */
+#define AMS_UNKNOWN2	0x2C	/* unknown 2 */
+#define AMS_UNKNOWN3	0x2D	/* unknown 3 */
+#define AMS_VENDOR	0x2E	/* vendor */
+
+/* AMS commands - use with the AMS_COMMAND register */
+enum ams_cmd {
+	AMS_CMD_NOOP = 0,
+	AMS_CMD_VERSION,
+	AMS_CMD_READMEM,
+	AMS_CMD_WRITEMEM,
+	AMS_CMD_ERASEMEM,
+	AMS_CMD_READEE,
+	AMS_CMD_WRITEEE,
+	AMS_CMD_RESET,
+	AMS_CMD_START,
+};
+
+struct ams {
+	char			init;		/* has it been initialized ? */
+	int			bus;		/* i2c bus */
+	int			address;	/* i2c address */
+	struct i2c_client	client;		/* i2c client */
+	struct of_device	*of_dev;	/* sysfs device */
+	char			vflag;		/* use orient1 or orient2 ? */
+	u32			orient1;	/* orientation words */
+	u32			orient2;
+	int			irq1;		/* first irq line */
+	int			irq2;		/* second irq line */
+	struct work_struct	worker;		/* worker thread */
+	struct input_dev	*idev;		/* input device */
+	int			xcalib;		/* calibrated null value for x */
+	int			ycalib;		/* calibrated null value for y */
+	int			zcalib;		/* calibrated null value for z */
+	struct mutex		input_mutex;
+	struct task_struct	*kthread;	/* kthread for input */
+};
+
+static struct ams ams;
+
+static int ams_attach(struct i2c_adapter *adapter);
+static int ams_detach(struct i2c_adapter *adapter);
+
+static struct i2c_driver ams_driver = {
+	.driver = {
+		.name	= "ams",
+		.owner	= THIS_MODULE,
+	},
+	.attach_adapter	= ams_attach,
+	.detach_adapter	= ams_detach,
+};
+
+static inline s32 ams_read(u8 reg)
+{
+	return i2c_smbus_read_byte_data(&ams.client, reg);
+}
+
+static inline int ams_write(u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(&ams.client, reg, value);
+}
+
+static int ams_cmd(enum ams_cmd cmd)
+{
+	s32 result;
+	int i;
+
+	ams_write(AMS_COMMAND, cmd);
+	for (i = 0; i < 10; i++) {
+		mdelay(5);
+		result = ams_read(AMS_COMMAND);
+		if (result = 0 || result & 0x80)
+			return 0;
+	}
+	return -1;
+}
+
+static void ams_sensors(s8 *x, s8 *y, s8 *z)
+{
+	u32 orient;
+
+	*x = ams_read(AMS_DATAX);
+	*y = ams_read(AMS_DATAY);
+	*z = ams_read(AMS_DATAZ);
+
+	orient = ams.vflag ? ams.orient2 : ams.orient1;
+	if (orient & 0x80) {
+		s8 tmp = *x;
+		*x = *y;
+		*y = tmp;
+	}
+	if (orient & 0x04)
+		*z = ~*z;
+	if (orient & 0x02)
+		*y = ~*y;
+	if (orient & 0x01)
+		*x = ~*x;
+
+	/* printk(KERN_DEBUG "ams: Sensors (%d, %d, %d)\n", *x, *y, *z); */
+}
+
+static ssize_t ams_show_x(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int retval;
+	s8 x, y, z;
+
+	ams_sensors(&x, &y, &z);
+	retval = snprintf(buf, PAGE_SIZE, "%d\n", x);
+	return retval;
+}
+static DEVICE_ATTR(x, S_IRUGO, ams_show_x, NULL);
+
+static ssize_t ams_show_y(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int retval;
+	s8 x, y, z;
+
+	ams_sensors(&x, &y, &z);
+	retval = snprintf(buf, PAGE_SIZE, "%d\n", y);
+	return retval;
+}
+static DEVICE_ATTR(y, S_IRUGO, ams_show_y, NULL);
+
+static ssize_t ams_show_z(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int retval;
+	s8 x, y, z;
+
+	ams_sensors(&x, &y, &z);
+	retval = snprintf(buf, PAGE_SIZE, "%d\n", z);
+	return retval;
+}
+
+static DEVICE_ATTR(z, S_IRUGO, ams_show_z, NULL);
+
+static int ams_mouse_kthread(void *data)
+{
+	s8 x, y, z;
+
+	while (!kthread_should_stop()) {
+		ams_sensors(&x, &y, &z);
+
+		input_report_abs(ams.idev, ABS_X, x - ams.xcalib);
+		input_report_abs(ams.idev, ABS_Y, y - ams.ycalib);
+		input_report_abs(ams.idev, ABS_Z, z - ams.zcalib);
+
+		input_sync(ams.idev);
+
+		msleep(25);
+	}
+	return 0;
+}
+
+static void ams_mouse_enable(void)
+{
+	s8 x, y, z;
+
+	mutex_lock(&ams.input_mutex);
+
+	if (ams.idev)
+		goto out;
+
+	ams_sensors(&x, &y, &z);
+	ams.xcalib = x;
+	ams.ycalib = y;
+	ams.zcalib = z;
+
+	ams.idev = input_allocate_device();
+	if (!ams.idev)
+		goto out;
+
+	ams.idev->name = "Apple Motion Sensor";
+	ams.idev->id.bustype = BUS_I2C;
+	ams.idev->id.vendor = 0;
+
+	input_set_abs_params(ams.idev, ABS_X, -50, 50, 3, 0);
+	input_set_abs_params(ams.idev, ABS_Y, -50, 50, 3, 0);
+	input_set_abs_params(ams.idev, ABS_Z, -50, 50, 3, 0);
+
+	set_bit(EV_ABS, ams.idev->evbit);
+	set_bit(EV_KEY, ams.idev->evbit);
+	set_bit(BTN_TOUCH, ams.idev->keybit);
+
+	if (input_register_device(ams.idev)) {
+		input_free_device(ams.idev);
+		ams.idev = NULL;
+		goto out;
+	}
+
+	ams.kthread = kthread_run(ams_mouse_kthread, NULL, "kams");
+	if (IS_ERR(ams.kthread)) {
+		input_unregister_device(ams.idev);
+		ams.idev = NULL;
+		goto out;
+	}
+out:
+	mutex_unlock(&ams.input_mutex);
+}
+
+static void ams_mouse_disable(void)
+{
+	mutex_lock(&ams.input_mutex);
+
+	if (!ams.idev)
+		goto out;
+
+	kthread_stop(ams.kthread);
+
+	input_unregister_device(ams.idev);
+
+	ams.idev = NULL;
+out:
+	mutex_unlock(&ams.input_mutex);
+}
+
+static ssize_t ams_show_mouse(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	return sprintf(buf, "%d\n", mouse);
+}
+
+static ssize_t ams_store_mouse(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	if (sscanf(buf, "%d\n", &mouse) != 1)
+		return -EINVAL;
+
+	mouse = !!mouse;
+
+	if (mouse)
+		ams_mouse_enable();
+	else
+		ams_mouse_disable();
+
+	return count;
+}
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, ams_show_mouse, ams_store_mouse);
+
+static void ams_worker(void *data)
+{
+	/* TODO: park hard disk drives like OSX ? */
+	if (ams_read(AMS_FREEFALL) & 0x80)
+		printk(KERN_INFO "ams: freefall interrupt\n");
+	if (ams_read(AMS_SHOCK) & 0x80)
+		printk(KERN_INFO "ams: shock interrupt\n");
+
+	ams_write(AMS_FREEFALL, 0);
+	ams_write(AMS_SHOCK, 0);
+}
+
+static irqreturn_t ams_interrupt(int irq, void *devid, struct pt_regs *regs)
+{
+	if (ams.init)
+		schedule_work(&ams.worker);
+	return IRQ_HANDLED;
+}
+
+static int ams_attach(struct i2c_adapter *adapter)
+{
+	int vmaj, vmin;
+	unsigned long bus;
+
+	if (ams.init)
+		return 0;
+
+	if (strncmp(adapter->name, "uni-n", 5))
+		return -ENODEV;
+	bus = simple_strtoul(adapter->name + 6, NULL, 10);
+	if (bus != ams.bus)
+		return -ENODEV;
+
+	ams.client.addr = ams.address;
+	ams.client.adapter = adapter;
+	ams.client.driver = &ams_driver;
+	strcpy(ams.client.name, "Apple Motion Sensor");
+
+	if (ams_cmd(AMS_CMD_RESET)) {
+		printk(KERN_INFO "ams: Failed to reset the device\n");
+		return -ENODEV;
+	}
+
+	if (ams_cmd(AMS_CMD_START)) {
+		printk(KERN_INFO "ams: Failed to start the device\n");
+		return -ENODEV;
+	}
+
+	/* get version/vendor information */
+	ams_write(AMS_CTRL1, 0x02);
+	ams_write(AMS_CTRL2, 0x85);
+	ams_write(AMS_CTRL3, 0x01);
+
+	ams_cmd(AMS_CMD_READMEM);
+
+	vmaj = ams_read(AMS_DATA1);
+	vmin = ams_read(AMS_DATA2);
+	if (vmaj != 1 || vmin != 52) {
+		printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
+		       vmaj, vmin);
+		return -ENODEV;
+	}
+
+	ams_cmd(AMS_CMD_VERSION);
+
+	vmaj = ams_read(AMS_DATA1);
+	vmin = ams_read(AMS_DATA2);
+	if (vmaj != 0 || vmin != 1) {
+		printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
+		       vmaj, vmin);
+		return -ENODEV;
+	}
+
+	if (ams_read(AMS_VENDOR) & 0x10)
+		ams.vflag = 1;
+
+	/* write initial values */
+	ams_write(AMS_SENSLOW, 0x15);
+	ams_write(AMS_SENSHIGH, 0x60);
+	ams_write(AMS_CTRLX, 0x08);
+	ams_write(AMS_CTRLY, 0x0F);
+	ams_write(AMS_CTRLZ, 0x4F);
+	ams_write(AMS_UNKNOWN1, 0x14);
+
+	ams_write(AMS_FREEFALL, 0);
+	ams_write(AMS_SHOCK, 0);
+
+	/* enable interrupts */
+	ams_write(AMS_CTRLX, 0x88);
+	ams_write(AMS_CTRLY, 0x8F);
+	ams_write(AMS_CTRLZ, 0xCF);
+
+	if (i2c_attach_client(&ams.client)) {
+		printk(KERN_INFO "ams: Failed to attach the client\n");
+		return -ENODEV;
+	}
+
+	if (mouse)
+		ams_mouse_enable();
+
+	ams.init = 1;
+
+	printk(KERN_INFO "ams: Apple Motion Sensor enabled\n");
+
+	return 0;
+}
+
+static int ams_detach(struct i2c_adapter *adapter)
+{
+	if (!ams.init)
+		return 0;
+
+	if (ams.idev)
+		ams_mouse_disable();
+
+	i2c_detach_client(&ams.client);
+
+	/* disable and ack interrupts */
+	ams_write(AMS_CTRLX, 0x08);
+	ams_write(AMS_CTRLY, 0x0F);
+	ams_write(AMS_CTRLZ, 0x4F);
+	ams_write(AMS_FREEFALL, 0);
+	ams_write(AMS_SHOCK, 0);
+
+	ams.init = 0;
+
+	printk(KERN_INFO "ams: Apple Motion Sensor disabled\n");
+
+	return 0;
+}
+
+static int __init ams_init(void)
+{
+	struct device_node* np;
+	u32 *prop;
+	int retval = -ENODEV;
+
+	np = of_find_node_by_name(NULL, "accelerometer");
+	if (!np)
+		goto out;
+	if (!device_is_compatible(np, "AAPL,accelerometer_1"))
+		goto out;
+
+	prop = (u32 *)get_property(np, "orientation", NULL);
+	if (!prop) {
+		retval = -EIO;
+		goto out;
+	}
+	ams.orient1 = *prop;
+	ams.orient2 = *(prop + 1);
+
+	prop = (u32 *)get_property(np, "reg", NULL);
+	if (!prop)
+		goto out;
+
+	/* look for bus either by path or using "reg" */
+	if (strstr(np->full_name, "/i2c-bus@") != NULL) {
+		const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9);
+		ams.bus = tmp_bus[0]-'0';
+	} else {
+		ams.bus = ((*prop) >> 8) & 0x0f;
+	}
+	ams.address = ((*prop) & 0xff) >> 1;
+
+	np = of_find_node_by_name(NULL, "accelerometer-1");
+	if (!np || np->n_intrs < 1)
+		goto out;
+
+	ams.irq1 = np->intrs[0].line;
+
+	np = of_find_node_by_name(NULL, "accelerometer-2");
+	if (!np || np->n_intrs < 1)
+		goto out;
+
+	ams.irq2 = np->intrs[0].line;
+
+	if (request_irq(ams.irq1, ams_interrupt, 0, "accelerometer-1",
+			NULL < 0))
+		goto out;
+
+	if (request_irq(ams.irq2, ams_interrupt, 0, "accelerometer-2",
+			NULL < 0))
+		goto free_irq1;
+
+	INIT_WORK(&ams.worker, ams_worker, NULL);
+
+	if ((ams.of_dev = of_platform_device_create(np, "ams", NULL)) = NULL)
+		goto free_irq2;
+
+	mutex_init(&ams.input_mutex);
+
+	device_create_file(&ams.of_dev->dev, &dev_attr_x);
+	device_create_file(&ams.of_dev->dev, &dev_attr_y);
+	device_create_file(&ams.of_dev->dev, &dev_attr_z);
+	device_create_file(&ams.of_dev->dev, &dev_attr_mouse);
+
+	if (i2c_add_driver(&ams_driver) < 0)
+		goto free_device_files;
+
+	return 0;
+
+free_device_files:
+	device_remove_file(&ams.of_dev->dev, &dev_attr_x);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_y);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_z);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_mouse);
+	mutex_destroy(&ams.input_mutex);
+free_irq2:
+	free_irq(ams.irq2, NULL);
+free_irq1:
+	free_irq(ams.irq1, NULL);
+out:
+	return retval;
+}
+
+static void __exit ams_exit(void)
+{
+	i2c_del_driver(&ams_driver);
+
+	free_irq(ams.irq1, NULL);
+	free_irq(ams.irq2, NULL);
+
+	device_remove_file(&ams.of_dev->dev, &dev_attr_x);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_y);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_z);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_mouse);
+
+	mutex_destroy(&ams.input_mutex);
+
+	of_device_unregister(ams.of_dev);
+}
+
+module_init(ams_init);
+module_exit(ams_exit);
drivers/hwmon/Kconfig renamed from drivers/hwmon/Kconfig~apple-motion-sensor-driver +17 -0
diff -puN drivers/hwmon/Kconfig~apple-motion-sensor-driver drivers/hwmon/Kconfig
@@ -507,6 +507,23 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_AMS
+	tristate "Apple Motion Sensor (ams)"
+	depends on HWMON && INPUT && PPC32
+	default n
+	help
+	  This driver provides support for the Apple Motion Sensor (ams),
+	  which provides an accelerometer and other misc. data.
+	  Some Apple PowerBooks (the series the PowerBook5,6 falls into,
+	  later ones have a slightly different one the driver doesn't handle)
+	  are supported. The accelerometer data is readable via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of ams.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
drivers/hwmon/Makefile renamed from drivers/hwmon/Makefile~apple-motion-sensor-driver +1 -0
diff -puN drivers/hwmon/Makefile~apple-motion-sensor-driver drivers/hwmon/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025
 obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_AMS)	+= ams.o
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o

_

#3 lore raw
Quoted 6 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…

Below you find the latest revision of my AMS driver.

I was about to merge the below, then this comes along. Now what?

Michael's driver is a new version that adds support for non-i2c (PMU based) chips found on new machines. It would be nice to have it, though it definitely needs a bit of review from Stelian, the original author.

What I would suggest at this point is to merge Stelian's original driver, as you were about todo, then have Michael produce a patch against it to be reviewed that could then be merged later on. I'm fine having that "later on" still be in the 2.6.18 timeframe since we aren't talking about anything critical here, just a fairly minor driver update that is nice to have, but we can decide that later based on the reviews of Michael's changes).

Cheers,
Ben.

Quoted 616 lines

From: Stelian Pop <stelian at popies.net>

This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks (the series the PowerBook5,6 falls into, later ones have a slightly different one the driver doesn't handle) are supported. The accelerometer data is readable via sysfs.

This driver also provides an absolute input class device, allowing the laptop to act as a pinball machine-esque joystick.

In the future (once the proper API exist in the block layer) this driver will tell the laptop disk to protect its heads in the event of a catastrophic fall.

Signed-off-by Stelian Pop <stelian at popies.net>
Signed-off-by Johannes Berg <johannes at sipsolutions.net>
Cc Benjamin Herrenschmidt <benh at kernel.crashing.org>
Cc Paul Mackerras <paulus at samba.org>
Cc Dmitry Torokhov <dtor_core at ameritech.net>
Cc Robert Love <rml at novell.com>
Cc Jean Delvare <khali at linux-fr.org>
 drivers/hwmon/Kconfig  |   17 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ams.c    |  544 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 562 insertions(+)

diff -puN /dev/null drivers/hwmon/ams.c

drivers/hwmon/ams.c new file +544 -0
@@ -0,0 +1,544 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian at popies.net)
+ *
+ * Clean room implementation based on the reverse engineered OSX driver by
+ * Johannes Berg <johannes at sipsolutions.net>, documentation available at
+ * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+
+#include <asm/of_device.h>
+#include <asm/pmac_feature.h>
+
+MODULE_DESCRIPTION("Apple Motion Sensor driver");
+MODULE_AUTHOR("Stelian Pop");
+MODULE_LICENSE("GPL");
+
+static unsigned int mouse;
+module_param(mouse, bool, 0);
+MODULE_PARM_DESC(mouse, "enable the input class device on module load");
+
+/* AMS registers */
+#define	AMS_COMMAND	0x00	/* command register */
+#define AMS_STATUS	0x01	/* status register */
+#define AMS_CTRL1	0x02	/* read control 1 (number of values) */
+#define AMS_CTRL2	0x03	/* read control 2 (offset?) */
+#define AMS_CTRL3	0x04	/* read control 3 (size of each value?) */
+#define AMS_DATA1	0x05	/* read data 1 */
+#define AMS_DATA2	0x06	/* read data 2 */
+#define AMS_DATA3	0x07	/* read data 3 */
+#define AMS_DATA4	0x08	/* read data 4 */
+#define AMS_DATAX	0x20	/* data X */
+#define AMS_DATAY	0x21	/* data Y */
+#define AMS_DATAZ	0x22	/* data Z */
+#define AMS_FREEFALL	0x24	/* freefall int control */
+#define AMS_SHOCK	0x25	/* shock int control */
+#define AMS_SENSLOW	0x26	/* sensitivity low limit */
+#define AMS_SENSHIGH	0x27	/* sensitivity high limit */
+#define AMS_CTRLX	0x28	/* control X */
+#define AMS_CTRLY	0x29	/* control Y */
+#define AMS_CTRLZ	0x2A	/* control Z */
+#define AMS_UNKNOWN1	0x2B	/* unknown 1 */
+#define AMS_UNKNOWN2	0x2C	/* unknown 2 */
+#define AMS_UNKNOWN3	0x2D	/* unknown 3 */
+#define AMS_VENDOR	0x2E	/* vendor */
+
+/* AMS commands - use with the AMS_COMMAND register */
+enum ams_cmd {
+	AMS_CMD_NOOP = 0,
+	AMS_CMD_VERSION,
+	AMS_CMD_READMEM,
+	AMS_CMD_WRITEMEM,
+	AMS_CMD_ERASEMEM,
+	AMS_CMD_READEE,
+	AMS_CMD_WRITEEE,
+	AMS_CMD_RESET,
+	AMS_CMD_START,
+};
+
+struct ams {
+	char			init;		/* has it been initialized ? */
+	int			bus;		/* i2c bus */
+	int			address;	/* i2c address */
+	struct i2c_client	client;		/* i2c client */
+	struct of_device	*of_dev;	/* sysfs device */
+	char			vflag;		/* use orient1 or orient2 ? */
+	u32			orient1;	/* orientation words */
+	u32			orient2;
+	int			irq1;		/* first irq line */
+	int			irq2;		/* second irq line */
+	struct work_struct	worker;		/* worker thread */
+	struct input_dev	*idev;		/* input device */
+	int			xcalib;		/* calibrated null value for x */
+	int			ycalib;		/* calibrated null value for y */
+	int			zcalib;		/* calibrated null value for z */
+	struct mutex		input_mutex;
+	struct task_struct	*kthread;	/* kthread for input */
+};
+
+static struct ams ams;
+
+static int ams_attach(struct i2c_adapter *adapter);
+static int ams_detach(struct i2c_adapter *adapter);
+
+static struct i2c_driver ams_driver = {
+	.driver = {
+		.name	= "ams",
+		.owner	= THIS_MODULE,
+	},
+	.attach_adapter	= ams_attach,
+	.detach_adapter	= ams_detach,
+};
+
+static inline s32 ams_read(u8 reg)
+{
+	return i2c_smbus_read_byte_data(&ams.client, reg);
+}
+
+static inline int ams_write(u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(&ams.client, reg, value);
+}
+
+static int ams_cmd(enum ams_cmd cmd)
+{
+	s32 result;
+	int i;
+
+	ams_write(AMS_COMMAND, cmd);
+	for (i = 0; i < 10; i++) {
+		mdelay(5);
+		result = ams_read(AMS_COMMAND);
+		if (result = 0 || result & 0x80)
+			return 0;
+	}
+	return -1;
+}
+
+static void ams_sensors(s8 *x, s8 *y, s8 *z)
+{
+	u32 orient;
+
+	*x = ams_read(AMS_DATAX);
+	*y = ams_read(AMS_DATAY);
+	*z = ams_read(AMS_DATAZ);
+
+	orient = ams.vflag ? ams.orient2 : ams.orient1;
+	if (orient & 0x80) {
+		s8 tmp = *x;
+		*x = *y;
+		*y = tmp;
+	}
+	if (orient & 0x04)
+		*z = ~*z;
+	if (orient & 0x02)
+		*y = ~*y;
+	if (orient & 0x01)
+		*x = ~*x;
+
+	/* printk(KERN_DEBUG "ams: Sensors (%d, %d, %d)\n", *x, *y, *z); */
+}
+
+static ssize_t ams_show_x(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int retval;
+	s8 x, y, z;
+
+	ams_sensors(&x, &y, &z);
+	retval = snprintf(buf, PAGE_SIZE, "%d\n", x);
+	return retval;
+}
+static DEVICE_ATTR(x, S_IRUGO, ams_show_x, NULL);
+
+static ssize_t ams_show_y(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int retval;
+	s8 x, y, z;
+
+	ams_sensors(&x, &y, &z);
+	retval = snprintf(buf, PAGE_SIZE, "%d\n", y);
+	return retval;
+}
+static DEVICE_ATTR(y, S_IRUGO, ams_show_y, NULL);
+
+static ssize_t ams_show_z(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int retval;
+	s8 x, y, z;
+
+	ams_sensors(&x, &y, &z);
+	retval = snprintf(buf, PAGE_SIZE, "%d\n", z);
+	return retval;
+}
+
+static DEVICE_ATTR(z, S_IRUGO, ams_show_z, NULL);
+
+static int ams_mouse_kthread(void *data)
+{
+	s8 x, y, z;
+
+	while (!kthread_should_stop()) {
+		ams_sensors(&x, &y, &z);
+
+		input_report_abs(ams.idev, ABS_X, x - ams.xcalib);
+		input_report_abs(ams.idev, ABS_Y, y - ams.ycalib);
+		input_report_abs(ams.idev, ABS_Z, z - ams.zcalib);
+
+		input_sync(ams.idev);
+
+		msleep(25);
+	}
+	return 0;
+}
+
+static void ams_mouse_enable(void)
+{
+	s8 x, y, z;
+
+	mutex_lock(&ams.input_mutex);
+
+	if (ams.idev)
+		goto out;
+
+	ams_sensors(&x, &y, &z);
+	ams.xcalib = x;
+	ams.ycalib = y;
+	ams.zcalib = z;
+
+	ams.idev = input_allocate_device();
+	if (!ams.idev)
+		goto out;
+
+	ams.idev->name = "Apple Motion Sensor";
+	ams.idev->id.bustype = BUS_I2C;
+	ams.idev->id.vendor = 0;
+
+	input_set_abs_params(ams.idev, ABS_X, -50, 50, 3, 0);
+	input_set_abs_params(ams.idev, ABS_Y, -50, 50, 3, 0);
+	input_set_abs_params(ams.idev, ABS_Z, -50, 50, 3, 0);
+
+	set_bit(EV_ABS, ams.idev->evbit);
+	set_bit(EV_KEY, ams.idev->evbit);
+	set_bit(BTN_TOUCH, ams.idev->keybit);
+
+	if (input_register_device(ams.idev)) {
+		input_free_device(ams.idev);
+		ams.idev = NULL;
+		goto out;
+	}
+
+	ams.kthread = kthread_run(ams_mouse_kthread, NULL, "kams");
+	if (IS_ERR(ams.kthread)) {
+		input_unregister_device(ams.idev);
+		ams.idev = NULL;
+		goto out;
+	}
+out:
+	mutex_unlock(&ams.input_mutex);
+}
+
+static void ams_mouse_disable(void)
+{
+	mutex_lock(&ams.input_mutex);
+
+	if (!ams.idev)
+		goto out;
+
+	kthread_stop(ams.kthread);
+
+	input_unregister_device(ams.idev);
+
+	ams.idev = NULL;
+out:
+	mutex_unlock(&ams.input_mutex);
+}
+
+static ssize_t ams_show_mouse(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	return sprintf(buf, "%d\n", mouse);
+}
+
+static ssize_t ams_store_mouse(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	if (sscanf(buf, "%d\n", &mouse) != 1)
+		return -EINVAL;
+
+	mouse = !!mouse;
+
+	if (mouse)
+		ams_mouse_enable();
+	else
+		ams_mouse_disable();
+
+	return count;
+}
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, ams_show_mouse, ams_store_mouse);
+
+static void ams_worker(void *data)
+{
+	/* TODO: park hard disk drives like OSX ? */
+	if (ams_read(AMS_FREEFALL) & 0x80)
+		printk(KERN_INFO "ams: freefall interrupt\n");
+	if (ams_read(AMS_SHOCK) & 0x80)
+		printk(KERN_INFO "ams: shock interrupt\n");
+
+	ams_write(AMS_FREEFALL, 0);
+	ams_write(AMS_SHOCK, 0);
+}
+
+static irqreturn_t ams_interrupt(int irq, void *devid, struct pt_regs *regs)
+{
+	if (ams.init)
+		schedule_work(&ams.worker);
+	return IRQ_HANDLED;
+}
+
+static int ams_attach(struct i2c_adapter *adapter)
+{
+	int vmaj, vmin;
+	unsigned long bus;
+
+	if (ams.init)
+		return 0;
+
+	if (strncmp(adapter->name, "uni-n", 5))
+		return -ENODEV;
+	bus = simple_strtoul(adapter->name + 6, NULL, 10);
+	if (bus != ams.bus)
+		return -ENODEV;
+
+	ams.client.addr = ams.address;
+	ams.client.adapter = adapter;
+	ams.client.driver = &ams_driver;
+	strcpy(ams.client.name, "Apple Motion Sensor");
+
+	if (ams_cmd(AMS_CMD_RESET)) {
+		printk(KERN_INFO "ams: Failed to reset the device\n");
+		return -ENODEV;
+	}
+
+	if (ams_cmd(AMS_CMD_START)) {
+		printk(KERN_INFO "ams: Failed to start the device\n");
+		return -ENODEV;
+	}
+
+	/* get version/vendor information */
+	ams_write(AMS_CTRL1, 0x02);
+	ams_write(AMS_CTRL2, 0x85);
+	ams_write(AMS_CTRL3, 0x01);
+
+	ams_cmd(AMS_CMD_READMEM);
+
+	vmaj = ams_read(AMS_DATA1);
+	vmin = ams_read(AMS_DATA2);
+	if (vmaj != 1 || vmin != 52) {
+		printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
+		       vmaj, vmin);
+		return -ENODEV;
+	}
+
+	ams_cmd(AMS_CMD_VERSION);
+
+	vmaj = ams_read(AMS_DATA1);
+	vmin = ams_read(AMS_DATA2);
+	if (vmaj != 0 || vmin != 1) {
+		printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
+		       vmaj, vmin);
+		return -ENODEV;
+	}
+
+	if (ams_read(AMS_VENDOR) & 0x10)
+		ams.vflag = 1;
+
+	/* write initial values */
+	ams_write(AMS_SENSLOW, 0x15);
+	ams_write(AMS_SENSHIGH, 0x60);
+	ams_write(AMS_CTRLX, 0x08);
+	ams_write(AMS_CTRLY, 0x0F);
+	ams_write(AMS_CTRLZ, 0x4F);
+	ams_write(AMS_UNKNOWN1, 0x14);
+
+	ams_write(AMS_FREEFALL, 0);
+	ams_write(AMS_SHOCK, 0);
+
+	/* enable interrupts */
+	ams_write(AMS_CTRLX, 0x88);
+	ams_write(AMS_CTRLY, 0x8F);
+	ams_write(AMS_CTRLZ, 0xCF);
+
+	if (i2c_attach_client(&ams.client)) {
+		printk(KERN_INFO "ams: Failed to attach the client\n");
+		return -ENODEV;
+	}
+
+	if (mouse)
+		ams_mouse_enable();
+
+	ams.init = 1;
+
+	printk(KERN_INFO "ams: Apple Motion Sensor enabled\n");
+
+	return 0;
+}
+
+static int ams_detach(struct i2c_adapter *adapter)
+{
+	if (!ams.init)
+		return 0;
+
+	if (ams.idev)
+		ams_mouse_disable();
+
+	i2c_detach_client(&ams.client);
+
+	/* disable and ack interrupts */
+	ams_write(AMS_CTRLX, 0x08);
+	ams_write(AMS_CTRLY, 0x0F);
+	ams_write(AMS_CTRLZ, 0x4F);
+	ams_write(AMS_FREEFALL, 0);
+	ams_write(AMS_SHOCK, 0);
+
+	ams.init = 0;
+
+	printk(KERN_INFO "ams: Apple Motion Sensor disabled\n");
+
+	return 0;
+}
+
+static int __init ams_init(void)
+{
+	struct device_node* np;
+	u32 *prop;
+	int retval = -ENODEV;
+
+	np = of_find_node_by_name(NULL, "accelerometer");
+	if (!np)
+		goto out;
+	if (!device_is_compatible(np, "AAPL,accelerometer_1"))
+		goto out;
+
+	prop = (u32 *)get_property(np, "orientation", NULL);
+	if (!prop) {
+		retval = -EIO;
+		goto out;
+	}
+	ams.orient1 = *prop;
+	ams.orient2 = *(prop + 1);
+
+	prop = (u32 *)get_property(np, "reg", NULL);
+	if (!prop)
+		goto out;
+
+	/* look for bus either by path or using "reg" */
+	if (strstr(np->full_name, "/i2c-bus@") != NULL) {
+		const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9);
+		ams.bus = tmp_bus[0]-'0';
+	} else {
+		ams.bus = ((*prop) >> 8) & 0x0f;
+	}
+	ams.address = ((*prop) & 0xff) >> 1;
+
+	np = of_find_node_by_name(NULL, "accelerometer-1");
+	if (!np || np->n_intrs < 1)
+		goto out;
+
+	ams.irq1 = np->intrs[0].line;
+
+	np = of_find_node_by_name(NULL, "accelerometer-2");
+	if (!np || np->n_intrs < 1)
+		goto out;
+
+	ams.irq2 = np->intrs[0].line;
+
+	if (request_irq(ams.irq1, ams_interrupt, 0, "accelerometer-1",
+			NULL < 0))
+		goto out;
+
+	if (request_irq(ams.irq2, ams_interrupt, 0, "accelerometer-2",
+			NULL < 0))
+		goto free_irq1;
+
+	INIT_WORK(&ams.worker, ams_worker, NULL);
+
+	if ((ams.of_dev = of_platform_device_create(np, "ams", NULL)) = NULL)
+		goto free_irq2;
+
+	mutex_init(&ams.input_mutex);
+
+	device_create_file(&ams.of_dev->dev, &dev_attr_x);
+	device_create_file(&ams.of_dev->dev, &dev_attr_y);
+	device_create_file(&ams.of_dev->dev, &dev_attr_z);
+	device_create_file(&ams.of_dev->dev, &dev_attr_mouse);
+
+	if (i2c_add_driver(&ams_driver) < 0)
+		goto free_device_files;
+
+	return 0;
+
+free_device_files:
+	device_remove_file(&ams.of_dev->dev, &dev_attr_x);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_y);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_z);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_mouse);
+	mutex_destroy(&ams.input_mutex);
+free_irq2:
+	free_irq(ams.irq2, NULL);
+free_irq1:
+	free_irq(ams.irq1, NULL);
+out:
+	return retval;
+}
+
+static void __exit ams_exit(void)
+{
+	i2c_del_driver(&ams_driver);
+
+	free_irq(ams.irq1, NULL);
+	free_irq(ams.irq2, NULL);
+
+	device_remove_file(&ams.of_dev->dev, &dev_attr_x);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_y);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_z);
+	device_remove_file(&ams.of_dev->dev, &dev_attr_mouse);
+
+	mutex_destroy(&ams.input_mutex);
+
+	of_device_unregister(ams.of_dev);
+}
+
+module_init(ams_init);
+module_exit(ams_exit);

diff -puN drivers/hwmon/Kconfig~apple-motion-sensor-driver drivers/hwmon/Kconfig

drivers/hwmon/Kconfig renamed from drivers/hwmon/Kconfig~apple-motion-sensor-driver +17 -0
@@ -507,6 +507,23 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_AMS
+	tristate "Apple Motion Sensor (ams)"
+	depends on HWMON && INPUT && PPC32
+	default n
+	help
+	  This driver provides support for the Apple Motion Sensor (ams),
+	  which provides an accelerometer and other misc. data.
+	  Some Apple PowerBooks (the series the PowerBook5,6 falls into,
+	  later ones have a slightly different one the driver doesn't handle)
+	  are supported. The accelerometer data is readable via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of ams.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON

diff -puN drivers/hwmon/Makefile~apple-motion-sensor-driver drivers/hwmon/Makefile

drivers/hwmon/Makefile renamed from drivers/hwmon/Makefile~apple-motion-sensor-driver +1 -0
@@ -18,6 +18,7 @@ obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025
 obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_AMS)	+= ams.o
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o

_

#4 lore raw
Quoted 12 lines · On Mon, 03 Jul 2006 13:47:28 +1000 Benjamin Herrenschmidt <benh at…
Quoted 6 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…

Below you find the latest revision of my AMS driver.

I was about to merge the below, then this comes along. Now what?

Michael's driver is a new version that adds support for non-i2c (PMU based) chips found on new machines. It would be nice to have it, though it definitely needs a bit of review from Stelian, the original author.

Quoted 7 lines

What I would suggest at this point is to merge Stelian's original driver, as you were about todo, then have Michael produce a patch against it to be reviewed that could then be merged later on. I'm fine having that "later on" still be in the 2.6.18 timeframe since we aren't talking about anything critical here, just a fairly minor driver update that is nice to have, but we can decide that later based on the reviews of Michael's changes).

Yes, but it wouldn't be a "patch against it" - Michael's patch adds multiple files with different names. So it would be a revert-and-redo.

#5 lore raw
In reply to Andrew Morton

Hello Andrew

Quoted 11 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…

Below you find the latest revision of my AMS driver.

I was about to merge the below, then this comes along. Now what?

From: Stelian Pop <stelian at popies.net>

This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks

I just noticed yesterday that Stelian sent a patch to lkml in May. My work is based on his separate driver from his website.

Given the fact that my driver includes all of his functionality and that replacing his with mine later in the process would mean to remove whole files again, I'd suggest to wait until I've fixed the outstanding issues (as seen in this thread) and then to merge mine.

Greets,
Michael

#6 lore raw

Le lundi 03 juillet 2006 ? 08:56 +0200, Michael Hanselmann a ?crit :

Quoted 15 lines

Hello Andrew

Quoted 11 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…

Below you find the latest revision of my AMS driver.

I was about to merge the below, then this comes along. Now what?

From: Stelian Pop <stelian at popies.net>

This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks

Quoted 7 lines

I just noticed yesterday that Stelian sent a patch to lkml in May. My work is based on his separate driver from his website.

Given the fact that my driver includes all of his functionality and that replacing his with mine later in the process would mean to remove whole files again, I'd suggest to wait until I've fixed the outstanding issues (as seen in this thread) and then to merge mine.

I have no problems with this approach. I'll take a look at your patch ASAP.

Stelian.

Signature · 1 line
Stelian Pop <stelian at popies.net>
#7 lore raw
In reply to Michael Hanselmann

Hi Michael,

Quoted 7 lines

Below you find the latest revision of my AMS driver. AMS stands for Apple Motion Sensor and it's included in the 2005 revisions of Apple iBooks and PowerBooks. The driver implements the PMU and I2C variants. MacBooks have another variant, which is not handled by this driver, mainly because it's totally different and I don't have access to a MacBook.

Quoted 6 lines

Some of the code is based on the I2C ams driver from Stelian Pop, whom I'd like to thank for his work.

HD parking is marked BROKEN as of now, because it'll need changes to the block, IDE and SCSI layers. The infrastructure works, tough (tested with my own HD parking code). That's why I left it in there.

I'd rather leave it out for now, and merge it when it has a chance to work. Merging non-working code is confusing at best.

Quoted 3 lines

I want this driver to be included in -mm as soon as possible, to get test feedback and to get it included in 2.6.19 (or maybe 2.6.18? Who knows. ;)). Thus I'd like to get your comments, suggestions, etc. on it.

2.6.19 at best.

drivers/hwmon/Kconfig config SENSORS_HDAPS · line 512

Quoted 10 lines

diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Kconfig linux-2.6.17-git20/drivers/hwmon/Kconfig

drivers/hwmon/Kconfig +3 -0
@@ -507,6 +507,42 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_AMS
+	tristate "Motion sensor driver"
+	default y

No, not everyone has this device. We don't have a default for other hardware monitoring drivers.

This should depend on HWMON, and probably EXPERIMENTAL too, until it gets some wider testing.

Also please respect the alphabetical order.

Quoted 40 lines
drivers/hwmon/Kconfig +33 -0
+	help
+	  Support for the motion sensor included in PowerBooks.
+
+config SENSORS_AMS_PMU
+	bool "PMU variant"
+	depends on SENSORS_AMS && ADB_PMU
+	default y
+	help
+	  PMU variant of motion sensor, found in late 2005 PowerBooks.
+
+config SENSORS_AMS_I2C
+	bool "I2C variant"
+	depends on SENSORS_AMS && I2C
+	default y
+	help
+	  I2C variant of motion sensor, found in early 2005 PowerBooks and
+	  iBooks.
+
+config SENSORS_AMS_MOUSE
+	bool "Support for mouse mode with motion sensor"
+	depends on SENSORS_AMS && INPUT
+	help
+	  Support for mouse emulation with motion sensor.
+
+config SENSORS_AMS_HDPARK
+	bool "Park hard disk heads on freefall or shock"
+	depends on SENSORS_AMS = y && BROKEN
+	default y
+	help
+	  Park the internal hard disk's heads if a free fall or shock is
+	  detected. This can help to prevent data loss in case of a
+	  head crash, altough nobody can guarantee anything.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON

diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Makefile linux-2.6.17-git20/drivers/hwmon/Makefile

drivers/hwmon/Makefile obj-$(CONFIG_SENSORS_VT8231) += vt8231.o · line 57

Quoted 9 lines
drivers/hwmon/Makefile +6 -0
@@ -49,6 +49,13 @@ obj-$(CONFIG_SENSORS_VT8231)	+= vt8231.o
 obj-$(CONFIG_SENSORS_W83627EHF)	+= w83627ehf.o
 obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
 
+ams-y					:= ams-core.o
+ams-$(CONFIG_SENSORS_AMS_PMU)		+= ams-pmu.o
+ams-$(CONFIG_SENSORS_AMS_I2C)		+= ams-i2c.o
+ams-$(CONFIG_SENSORS_AMS_MOUSE)		+= ams-mouse.o
+ams-$(CONFIG_SENSORS_AMS_HDPARK)	+= ams-hdpark.o
+obj-$(CONFIG_SENSORS_AMS)		+= ams.o

If you are going to have many source files and a composite module, please create your own subdirectory under drivers/hwmon and put all your stuff here. The kernel build system is notoriously bad at handling multiple composite modules within the same subdirectory.

I won't have the time to review such a big and complex driver within several weeks, so people who want this driver in 2.6.19 will have to review it by themselves.

Thanks,

Signature · 1 line
Jean Delvare
#8 lore raw
Quoted 3 lines

I'd rather leave it out for now, and merge it when it has a chance to work. Merging non-working code is confusing at best.

:)

Quoted 5 lines
Quoted 3 lines

I want this driver to be included in -mm as soon as possible, to get test feedback and to get it included in 2.6.19 (or maybe 2.6.18? Who knows. ;)). Thus I'd like to get your comments, suggestions, etc. on it.

2.6.19 at best.

Oh, it's just a driver update for some powermac specific thingy, so depending how long 2.6.18 takes, I have no problem as the powermac maintainer letting that in once it's been properly fixed and reviewed. (Which I don't have time to do right now neither, but possibly later).

There shouldn't be significant changes to the i2c side of it from Stelian's driver anyway, more like a new backend using the PMU.

Quoted 11 lines

drivers/hwmon/Kconfig config SENSORS_HDAPS · line 512

Quoted 3 lines
drivers/hwmon/Kconfig +3 -0
+config SENSORS_AMS
+	tristate "Motion sensor driver"
+	default y

No, not everyone has this device. We don't have a default for other hardware monitoring drivers.

This should depend on HWMON, and probably EXPERIMENTAL too, until it gets some wider testing.

Also please respect the alphabetical order.

It can stay default y as lons as it depends on PPC_PMAC (which it should do anyway)

Quoted 4 lines

If you are going to have many source files and a composite module, please create your own subdirectory under drivers/hwmon and put all your stuff here. The kernel build system is notoriously bad at handling multiple composite modules within the same subdirectory.

Agreed.

Cheers,
Ben.

#9 lore raw
In reply to Michael Hanselmann

Le lundi 03 juillet 2006 ? 00:26 +0200, Michael Hanselmann a ?crit :

Quoted 1 line

[AMS patch]

Okay, I've tried the driver and it oopsed on rmmod:

Unable to handle kernel paging request for data at address 0x00000000
Faulting instruction address: 0xc01cfd1c
Oops: Kernel access of bad area, sig: 11 [#1]

Modules linked in: ams nfsd exportfs lockd sunrpc appletouch bcm43xx ohci1394 ie ee80211softmac ieee80211 pcmcia ieee80211_crypt yenta_socket rsrc_nonstatic pcmc ia_core ohci_hcd ehci_hcd
NIP: C01CFD1C LR: C01CFCFC CTR: C01CFC6C
REGS: c48b9d90 TRAP: 0300   Not tainted  (2.6.17.2)
MSR: 00009032 <EE,ME,IR,DR>  CR: 22004422  XER: 20000000
DAR: 00000000, DSISR: 42000000

TASK = dd79b340[26173] 'rmmod' THREAD: c48b8000
GPR00: 00100100 C48B9E40 DD79B340 C0CDA828 C00F4CD0 00000001 C48B8000 F22451F8
GPR08: 00000000 00200200 00000000 F2245238 00000000 1001A188 100D0000 00000000
GPR16: 00000000 10104308 100D0000 100B0000 100D0000 100B0000 10013008 00000000
GPR24: C0380000 C0380000 EFFCCC88 C0350000 C0CDA828 F22450D0 00000000 F22450B8 NIP [C01CFD1C] i2c_detach_client+0xb0/0x16c
LR [C01CFCFC] i2c_detach_client+0x90/0x16c

Call Trace:

[C48B9E40] [DDF8077C] 0xddf8077c (unreliable) [C48B9E60] [F2242984] ams_i2c_detach+0x20/0x60 [ams] [C48B9E80] [C01D1024] i2c_del_driver+0x60/0x1d0 [C48B9EB0] [F22429DC] ams_i2c_exit+0x18/0x28 [ams] [C48B9EC0] [F2242428] cleanup_module+0x58/0xd4 [ams] [C48B9ED0] [C0045C70] sys_delete_module+0x180/0x1e8 [C48B9F40] [C000EE7C] ret_from_syscall+0x0/0x38 --- Exception: c01 at 0xff7320c

    LR = 0x1000103c

Instruction dump:
3b9c0028 3bbf0018 7f83e378 480c4839 38ff0140 815f0140 3c000010 81070004 60000100 3d200020 397f0180 61290200 <91480000> 3c80c038 7fa3eb78 901f0140

I haven't had the time too look into the issue yet.

Below are a few comments on the patch:

drivers/hwmon/ams.h line 56

Quoted 2 lines
drivers/hwmon/ams.h +2 -0
+	/* calibrated null values */
+	int xcalib, ycalib;

Add zcalib too for movement on z-axis.

drivers/hwmon/ams-mouse.c line 36

Quoted 2 lines
drivers/hwmon/ams-mouse.c +2 -0
+		input_report_abs(ams.idev, ABS_X, x - ams.xcalib);
+		input_report_abs(ams.idev, ABS_Y, y - ams.ycalib);

Ditto, ABS_Z...

drivers/hwmon/ams-mouse.c line 69

Quoted 2 lines
drivers/hwmon/ams-mouse.c +2 -0
+	input_set_abs_params(ams.idev, ABS_X, -50, 50, 3, 0);
+	input_set_abs_params(ams.idev, ABS_Y, -50, 50, 3, 0);

And here...

Quoted 10 lines
drivers/hwmon/ams-mouse.c +10 -0
+static ssize_t ams_mouse_store_mouse(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int oldmouse = mouse;
+
+	if (sscanf(buf, "%d\n", &mouse) != 1)
+		return -EINVAL;
+
+	mouse = !!mouse;
+

drivers/hwmon/ams-mouse.c line 124

Quoted 10 lines
drivers/hwmon/ams-mouse.c +10 -0
+	if (oldmouse != mouse) {
+		mutex_lock(&ams.lock);
+
+		if (mouse)
+			ams_mouse_enable();
+		else
+			ams_mouse_disable();
+
+		mutex_unlock(&ams.lock);
+	}

No need for oldmouse here. Just call ams_mouse_enable() or disable() in every case.

drivers/hwmon/ams-mouse.c line 130

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+	ams_mouse_show_mouse, ams_mouse_store_mouse);

I would prefer three different files for x, y and z instead of a single one...

drivers/hwmon/ams-pmu.c line 173

Quoted 4 lines
drivers/hwmon/ams-pmu.c +4 -0
+	result = ams_sensor_attach();
+	if (result < 0) {
+		goto exit;
+	}

No need for braces.

drivers/hwmon/Kconfig config SENSORS_HDAPS · line 535

Quoted 5 lines
drivers/hwmon/Kconfig +5 -0
+config SENSORS_AMS_MOUSE
+	bool "Support for mouse mode with motion sensor"
+	depends on SENSORS_AMS && INPUT
+	help
+	  Support for mouse emulation with motion sensor.

Is it really necessary to have a config option here ?

Stelian.

Signature · 1 line
Stelian Pop <stelian at popies.net>
#10 lore raw
Quoted 2 lines

Is it really necessary to have a config option here ?

Same goes, imo, for the hdpark functionality (once we get it through all layers, I agree with leaving it out for now completely). That's what the thing was designed for after all.

johannes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 828 bytes
Desc: This is a digitally signed message part
Url : http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20060703/c013b9ad/attachment.bin

#11 lore raw
In reply to Stelian Pop
Quoted 6 lines · Stelian Pop

drivers/hwmon/ams-mouse.c line 130

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+	ams_mouse_show_mouse, ams_mouse_store_mouse);

I would prefer three different files for x, y and z instead of a single one...

Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the ams_sensors function gets much more complicated.

To prevent it from having to read them three times in a row, I joined all three values.

Do you think I should rewrite the ams_sensors function to only get the correct value?

Thanks,
Michael

(All other issues, including those in the other mails, are clear and will be addressed by a new patch)

#12 lore raw
Quoted 12 lines · Michael Hanselmann
Quoted 6 lines · Stelian Pop

drivers/hwmon/ams-mouse.c line 130

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+	ams_mouse_show_mouse, ams_mouse_store_mouse);

I would prefer three different files for x, y and z instead of a single one...

Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the ams_sensors function gets much more complicated.

Quoted 5 lines

To prevent it from having to read them three times in a row, I joined all three values.

Do you think I should rewrite the ams_sensors function to only get the correct value?

Cache the values and only re-read from the hardware after a given amount of time has elapsed ?

Quoted 5 lines

Thanks,
Michael

(All other issues, including those in the other mails, are clear and will be addressed by a new patch)

#13 lore raw

Le lundi 03 juillet 2006 ? 20:49 +1000, Benjamin Herrenschmidt a ?crit :

Quoted 13 lines · Michael Hanselmann
Quoted 12 lines · Stelian Pop
Quoted 6 lines

drivers/hwmon/ams-mouse.c line 130

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+	ams_mouse_show_mouse, ams_mouse_store_mouse);

I would prefer three different files for x, y and z instead of a single one...

Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the ams_sensors function gets much more complicated.

Quoted 8 lines
Quoted 5 lines

To prevent it from having to read them three times in a row, I joined all three values.

Do you think I should rewrite the ams_sensors function to only get the correct value?

Cache the values and only re-read from the hardware after a given amount of time has elapsed ?

Seems to be a good idea indeed.

Stelian.

Signature · 1 line
Stelian Pop <stelian at popies.net>
#14 lore raw
Quoted 17 lines · Stelian Pop

Le lundi 03 juillet 2006 ? 20:49 +1000, Benjamin Herrenschmidt a ?crit :

Quoted 16 lines · Michael Hanselmann
Quoted 15 lines · Stelian Pop
Quoted 6 lines

drivers/hwmon/ams-mouse.c line 130

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+	ams_mouse_show_mouse, ams_mouse_store_mouse);

I would prefer three different files for x, y and z instead of a single one...

Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the ams_sensors function gets much more complicated.

To prevent it from having to read them three times in a row, I joined all three values.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Do you think I should rewrite the ams_sensors function to only get the correct value?

Cache the values and only re-read from the hardware after a given amount of time has elapsed ?

Seems to be a good idea indeed.

But what is the purpose of that attribute anyway? I'd just drop it completely.

Signature · 1 line
Dmitry
#15 lore raw
In reply to Michael Hanselmann
Quoted 4 lines · Michael Hanselmann
Quoted 3 lines · Stelian Pop

drivers/hwmon/ams-mouse.c line 130

Quoted 3 lines
drivers/hwmon/ams-mouse.c +3 -0
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+	ams_mouse_show_mouse, ams_mouse_store_mouse);

[I think the following discussion is about ams_show_xyz, not ams_mouse_show_mouse.]

Quoted 9 lines
Quoted 2 lines

I would prefer three different files for x, y and z instead of a single one...

Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the ams_sensors function gets much more complicated.

To prevent it from having to read them three times in a row, I joined all three values.

I don't know what a software will be doing with it, but "displacement" (and its time derivatives "velocity", "acceleration", "jerk") is a vector. Why not write the component-wise representation of the vector into a single sysfs attribute? Especially if this keeps the kernel code simple and small. I suppose even userspace code which evaluates the attribute is in many cases simpler (and more precise anyway) if only a single attribute is used.

Signature · 3 lines
Stefan Richter
-===-=-=- -=- ==--
http://arcgraph.de/sr/
#16 lore raw
In reply to Michael Hanselmann
Quoted 3 lines · Michael Hanselmann

+???????set_bit(EV_ABS, ams.idev->evbit);
+???????set_bit(EV_KEY, ams.idev->evbit);
+???????set_bit(BTN_TOUCH, ams.idev->keybit);

BTW, what is BTN_TOUCH doing here? I don't see it being reported anywhere...

Just drop it off and have ams be a "joystick" instead of a "mouse" ;) Joystick interface is probably better suited anyway.

Signature · 1 line
Dmitry
#17 lore raw
In reply to Michael Hanselmann

Hello

Quoted 1 line · Michael Hanselmann

Below you find the latest revision of my AMS driver.

Here's another patch with lots of fixes. In random order:

- I2C crash: Moved client remove call, unable to test myself
- Z axis for mouse
- Removed config option for mouse
- Renamed "xyz" attribute to "current" after discussion with Johannes Berg (johill). I don't like caching here, thus one attribute with all values. Users might want to use it in scripts.
- Removed hdpark stuff for now
- Changed dependencies of SENSORS_AMS, moved to its own directory
- Set bustype correctly
- Cleaned up ams_sensors function

Is this better now?

Todo:
- Rewrite I2C attach code (I don't have the I2C hardware myself, thus I can't do it right now)
- Maybe rewrite mouse emulation without kthread, but with workqueue (Any comments on this?)

Thanks,
Michael

---

drivers/hwmon/ams/ams-core.c +242 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-core.c linux-2.6.17-git21/drivers/hwmon/ams/ams-core.c
@@ -0,0 +1,242 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian at popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel at hansmi.ch)
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/pmac_pfunc.h>
+
+#include "ams.h"
+
+/* There is only one motion sensor per machine */
+struct ams ams;
+
+static unsigned int verbose;
+module_param(verbose, bool, 0644);
+MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
+
+/* Call with lock held! */
+void ams_sensors(s8 *x, s8 *y, s8 *z)
+{
+	u32 orient = ams.vflag? ams.orient1 : ams.orient2;
+
+	if (orient & 0x80)
+		/* X and Y swapped */
+		ams.get_xyz(y, x, z);
+	else
+		ams.get_xyz(x, y, z);
+
+	if (orient & 0x04)
+		*z = ~(*z);
+	if (orient & 0x02)
+		*y = ~(*y);
+	if (orient & 0x01)
+		*x = ~(*x);
+}
+
+static ssize_t ams_show_current(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	s8 x, y, z;
+
+	mutex_lock(&ams.lock);
+	ams_sensors(&x, &y, &z);
+	mutex_unlock(&ams.lock);
+
+	return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z);
+}
+
+static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL);
+
+static void ams_handle_irq(void *data)
+{
+	enum ams_irq irq = *((enum ams_irq *)data);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ams.irq_lock, flags);
+
+	ams.worker_irqs |= irq;
+	schedule_work(&ams.worker);
+
+	spin_unlock_irqrestore(&ams.irq_lock, flags);
+}
+
+static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
+static struct pmf_irq_client ams_freefall_client = {
+	.owner = THIS_MODULE,
+	.handler = ams_handle_irq,
+	.data = &ams_freefall_irq_data,
+};
+
+static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
+static struct pmf_irq_client ams_shock_client = {
+	.owner = THIS_MODULE,
+	.handler = ams_handle_irq,
+	.data = &ams_shock_irq_data,
+};
+
+/* Once hard disk parking is implemented in the kernel, this function can
+ * trigger it.
+ */
+static void ams_worker(void *data)
+{
+	mutex_lock(&ams.lock);
+
+	if (ams.has_device) {
+		if (ams.worker_irqs & AMS_IRQ_FREEFALL) {
+			if (verbose)
+				printk(KERN_INFO "ams: freefall detected!\n");
+
+			ams.worker_irqs &= ~AMS_IRQ_FREEFALL;
+			ams.clear_irq(AMS_IRQ_FREEFALL);
+		}
+
+		if (ams.worker_irqs & AMS_IRQ_SHOCK) {
+			if (verbose)
+				printk(KERN_INFO "ams: shock detected!\n");
+
+			ams.worker_irqs &= ~AMS_IRQ_SHOCK;
+			ams.clear_irq(AMS_IRQ_SHOCK);
+		}
+	}
+
+	mutex_unlock(&ams.lock);
+}
+
+/* Call with lock held! */
+int ams_sensor_attach(void)
+{
+	int result;
+	u32 *prop;
+
+	/* Get orientation */
+	prop = (u32*)get_property(ams.of_node, "orientation", NULL);
+	if (!prop)
+		return -ENODEV;
+	ams.orient1 = *prop;
+	ams.orient2 = *(prop + 1);
+
+	/* Register freefall interrupt handler */
+	result = pmf_register_irq_client(ams.of_node,
+			"accel-int-1",
+			&ams_freefall_client);
+	if (result < 0)
+		return -ENODEV;
+
+	/* Reset saved irqs */
+	ams.worker_irqs = 0;
+
+	/* Register shock interrupt handler */
+	result = pmf_register_irq_client(ams.of_node,
+			"accel-int-2",
+			&ams_shock_client);
+	if (result < 0) {
+		pmf_unregister_irq_client(&ams_freefall_client);
+		return -ENODEV;
+	}
+
+	/* Create device */
+	ams.of_dev = of_platform_device_create(ams.of_node, "ams", NULL);
+	if (!ams.of_dev) {
+		pmf_unregister_irq_client(&ams_shock_client);
+		pmf_unregister_irq_client(&ams_freefall_client);
+		return -ENODEV;
+	}
+
+	/* Create attributes */
+	device_create_file(&ams.of_dev->dev, &dev_attr_current);
+
+	ams.vflag = !!(ams.get_vendor() & 0x10);
+
+	/* Init mouse device */
+	ams_mouse_init();
+
+	return 0;
+}
+
+int __init ams_init(void)
+{
+	struct device_node *np;
+
+	spin_lock_init(&ams.irq_lock);
+	mutex_init(&ams.lock);
+	INIT_WORK(&ams.worker, ams_worker, NULL);
+
+#ifdef CONFIG_SENSORS_AMS_I2C
+	np = of_find_node_by_name(NULL, "accelerometer");
+	if (np && device_is_compatible(np, "AAPL,accelerometer_1"))
+		/* Found I2C motion sensor */
+		return ams_i2c_init(np);
+#endif
+
+#ifdef CONFIG_SENSORS_AMS_PMU
+	np = of_find_node_by_name(NULL, "sms");
+	if (np && device_is_compatible(np, "sms"))
+		/* Found PMU motion sensor */
+		return ams_pmu_init(np);
+#endif
+
+	printk(KERN_ERR "ams: No motion sensor found.\n");
+
+	return -ENODEV;
+}
+
+void ams_exit(void)
+{
+	mutex_lock(&ams.lock);
+
+	if (ams.has_device) {
+		/* Remove mouse device */
+		ams_mouse_exit();
+
+		/* Shut down implementation */
+		ams.exit();
+
+		/* Cancel interrupt worker
+		 *
+		 * We do this after ams.exit(), because an interrupt might
+		 * have arrived before disabling them.
+		 */
+		cancel_delayed_work(&ams.worker);
+		flush_scheduled_work();
+
+		/* Remove attributes */
+		device_remove_file(&ams.of_dev->dev, &dev_attr_current);
+
+		/* Remove device */
+		of_device_unregister(ams.of_dev);
+
+		/* Remove handler */
+		pmf_unregister_irq_client(&ams_shock_client);
+		pmf_unregister_irq_client(&ams_freefall_client);
+	}
+
+	mutex_unlock(&ams.lock);
+}
+
+MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
+MODULE_DESCRIPTION("Apple Motion Sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ams_init);
+module_exit(ams_exit);
drivers/hwmon/ams/ams.h +71 -0

71 lines not on this page · raw message

drivers/hwmon/ams/ams-i2c.c +289 -0

289 lines not on this page · raw message

drivers/hwmon/ams/ams-mouse.c +145 -0

145 lines not on this page · raw message

drivers/hwmon/ams/ams-pmu.c +202 -0

202 lines not on this page · raw message

drivers/hwmon/ams/Makefile +8 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/ams/Makefile linux-2.6.17-git21/drivers/hwmon/ams/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for Apple Motion Sensor driver
+#
+
+ams-y					:= ams-core.o ams-mouse.o
+ams-$(CONFIG_SENSORS_AMS_PMU)		+= ams-pmu.o
+ams-$(CONFIG_SENSORS_AMS_I2C)		+= ams-i2c.o
+obj-$(CONFIG_SENSORS_AMS)		+= ams.o
drivers/hwmon/Kconfig +22 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/Kconfig linux-2.6.17-git21/drivers/hwmon/Kconfig
@@ -94,6 +94,28 @@ config SENSORS_ADM9240
 	  This driver can also be built as a module.  If so, the module
 	  will be called adm9240.
 
+config SENSORS_AMS
+	tristate "Apple Motion Sensor driver"
+	depends on HWMON && PPC_PMAC && INPUT && EXPERIMENTAL
+	default y
+	help
+	  Support for the motion sensor included in PowerBooks.
+
+config SENSORS_AMS_PMU
+	bool "PMU variant"
+	depends on SENSORS_AMS && ADB_PMU
+	default y
+	help
+	  PMU variant of motion sensor, found in late 2005 PowerBooks.
+
+config SENSORS_AMS_I2C
+	bool "I2C variant"
+	depends on SENSORS_AMS && I2C
+	default y
+	help
+	  I2C variant of motion sensor, found in early 2005 PowerBooks and
+	  iBooks.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on HWMON && I2C && EXPERIMENTAL
drivers/hwmon/Makefile +1 -0
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/Makefile linux-2.6.17-git21/drivers/hwmon/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025
 obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
#18 lore raw

drivers/hwmon/ams/ams-core.c line 82

Quoted 12 lines · you
drivers/hwmon/ams/ams-core.c +12 -0
+static void ams_handle_irq(void *data)
+{
+	enum ams_irq irq = *((enum ams_irq *)data);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ams.irq_lock, flags);
+
+	ams.worker_irqs |= irq;
+	schedule_work(&ams.worker);
+
+	spin_unlock_irqrestore(&ams.irq_lock, flags);
+}

I would say this is racy.
Locks should be added as shown below.

Review

Quoted 2 lines
(quoted diff) +2 -0
+static void ams_worker(void *data)
+{

unsigned long flags;

drivers/hwmon/ams/ams-core.c line 105

Quoted 3 lines
drivers/hwmon/ams/ams-core.c +3 -0
+	mutex_lock(&ams.lock);
+
+	if (ams.has_device) {

spin_lock_irqsave(&ams.irq_lock, flags);

Quoted 5 lines
drivers/hwmon/ams/ams-core.c +5 -0
+		if (ams.worker_irqs & AMS_IRQ_FREEFALL) {
+			if (verbose)
+				printk(KERN_INFO "ams: freefall detected!\n");
+
+			ams.worker_irqs &= ~AMS_IRQ_FREEFALL;

drivers/hwmon/ams/ams-core.c line 122

Quoted 10 lines
drivers/hwmon/ams/ams-core.c +10 -0
+			ams.clear_irq(AMS_IRQ_FREEFALL);
+		}
+
+		if (ams.worker_irqs & AMS_IRQ_SHOCK) {
+			if (verbose)
+				printk(KERN_INFO "ams: shock detected!\n");
+
+			ams.worker_irqs &= ~AMS_IRQ_SHOCK;
+			ams.clear_irq(AMS_IRQ_SHOCK);
+		

spin_unlock_irqrestore(&ams.irq_lock, flags);

drivers/hwmon/ams/ams-core.c line 67

Quoted 4 lines
drivers/hwmon/ams/ams-core.c +4 -0
+	}
+
+	mutex_unlock(&ams.lock);
+}

Otherwise an IRQ could trigger while the bottom half is executing and corrupt the ams.worker_irqs field, because it is modified non-atomically.

Signature · 1 line
Greetings Michael.