* [RFC] Apple Motion Sensor driver
@ 2006-07-02 22:26 Michael Hanselmann
2006-07-03 2:33 ` Dmitry Torokhov
` (5 more replies)
0 siblings, 6 replies; 19+ messages in thread
From: Michael Hanselmann @ 2006-07-02 22:26 UTC (permalink / raw)
To: linux-kernel
Cc: lm-sensors, khali, linux-kernel, benh, johannes, stelian,
chainsaw
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
---
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
--- linux-2.6.17-git20.orig/drivers/hwmon/ams-core.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git20/drivers/hwmon/ams-core.c 2006-07-02 22:19:31.000000000 +0200
@@ -0,0 +1,264 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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);
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/ams.h linux-2.6.17-git20/drivers/hwmon/ams.h
--- linux-2.6.17-git20.orig/drivers/hwmon/ams.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git20/drivers/hwmon/ams.h 2006-07-02 22:19:12.000000000 +0200
@@ -0,0 +1,92 @@
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <asm/of_device.h>
+
+enum ams_irq {
+ AMS_IRQ_FREEFALL = 0x01,
+ AMS_IRQ_SHOCK = 0x02,
+ AMS_IRQ_GLOBAL = 0x04,
+ AMS_IRQ_ALL =
+ AMS_IRQ_FREEFALL |
+ AMS_IRQ_SHOCK |
+ AMS_IRQ_GLOBAL,
+};
+
+struct ams {
+ /* Locks */
+ spinlock_t irq_lock;
+ struct mutex lock;
+
+ /* General properties */
+ struct device_node *of_node;
+ struct of_device *of_dev;
+ char has_device;
+ char vflag;
+ u32 orient1;
+ u32 orient2;
+
+ /* Interrupt worker */
+ struct work_struct worker;
+ u8 worker_irqs;
+
+ /* Implementation
+ *
+ * Only call these functions with the main lock held.
+ */
+ void (*exit)(void);
+
+ void (*get_xyz)(s8 *x, s8 *y, s8 *z);
+ u8 (*get_vendor)(void);
+
+ void (*clear_irq)(enum ams_irq reg);
+
+ /* I2C properties */
+ int i2c_bus;
+ int i2c_address;
+ struct i2c_client i2c_client;
+
+#ifdef CONFIG_SENSORS_AMS_MOUSE
+ /* Mouse emulation */
+ struct task_struct *kthread;
+ struct input_dev *idev;
+
+ /* calibrated null values */
+ int xcalib, ycalib;
+#endif
+
+#ifdef CONFIG_SENSORS_AMS_HDPARK
+ /* HD parking */
+ int is_parked;
+ unsigned long last_park_jiffies;
+ struct work_struct park_worker;
+#endif
+};
+
+extern struct ams ams;
+
+extern void ams_sensors(s8 *x, s8 *y, s8 *z);
+extern int ams_sensor_attach(void);
+
+extern int ams_pmu_init(struct device_node *np);
+extern int ams_i2c_init(struct device_node *np);
+
+#ifdef CONFIG_SENSORS_AMS_MOUSE
+extern void ams_mouse_init(void);
+extern void ams_mouse_exit(void);
+#else
+static inline void ams_mouse_init(void) {}
+static inline void ams_mouse_exit(void) {}
+#endif
+
+#ifdef CONFIG_SENSORS_AMS_HDPARK
+extern void ams_hdpark_init(void);
+extern void ams_hdpark_exit(void);
+extern void ams_hdpark(void);
+#else
+static inline void ams_hdpark_init(void) {}
+static inline void ams_hdpark_exit(void) {}
+static inline void ams_hdpark(void) {}
+#endif
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/ams-hdpark.c linux-2.6.17-git20/drivers/hwmon/ams-hdpark.c
--- linux-2.6.17-git20.orig/drivers/hwmon/ams-hdpark.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git20/drivers/hwmon/ams-hdpark.c 2006-07-02 22:21:46.000000000 +0200
@@ -0,0 +1,72 @@
+/*
+ * Apple Motion Sensor driver (hard disk head parking)
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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 code won't work until the IDE layer allows HD parking. When
+ * that's done, this code needs to be updated accordingly.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/param.h>
+#include <linux/hdpark.h>
+
+#include "ams.h"
+
+static void ams_park_worker(void *data)
+{
+ mutex_lock(&ams.lock);
+
+ if (ams.is_parked) {
+ if (time_after_eq(jiffies, ams.last_park_jiffies + HZ)) {
+ hdpark_end();
+ ams.is_parked = 0;
+ } else
+ /* Try again */
+ schedule_delayed_work(&ams.park_worker, HZ / 5);
+ }
+
+ mutex_unlock(&ams.lock);
+}
+
+/* Call with lock held! */
+void ams_hdpark()
+{
+ if (!ams.is_parked) {
+ ams.is_parked = 1;
+ hdpark_start();
+ }
+
+ ams.last_park_jiffies = jiffies;
+
+ /* Check in about 1.1 seconds. This is to avoid the time_after_eq
+ * in ams_park_worker not being true when it runs.
+ */
+ schedule_delayed_work(&ams.park_worker, HZ * 1.1);
+}
+
+/* Call with lock held! */
+void ams_hdpark_init()
+{
+ INIT_WORK(&ams.park_worker, ams_park_worker, NULL);
+}
+
+/* Call with lock held! */
+void ams_hdpark_exit()
+{
+ cancel_delayed_work(&ams.park_worker);
+ flush_scheduled_work();
+
+ if (ams.is_parked) {
+ hdpark_end();
+ ams.is_parked = 0;
+ }
+}
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/ams-i2c.c linux-2.6.17-git20/drivers/hwmon/ams-i2c.c
--- linux-2.6.17-git20.orig/drivers/hwmon/ams-i2c.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git20/drivers/hwmon/ams-i2c.c 2006-07-02 21:57:32.000000000 +0200
@@ -0,0 +1,284 @@
+/*
+ * Apple Motion Sensor driver (I2C variant)
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * Clean room implementation based on the reverse engineered Mac OS X driver by
+ * Johannes Berg <johannes@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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "ams.h"
+
+/* 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_i2c_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,
+};
+
+static int ams_i2c_attach(struct i2c_adapter *adapter);
+static int ams_i2c_detach(struct i2c_adapter *adapter);
+
+static struct i2c_driver ams_i2c_driver = {
+ .driver = {
+ .name = "ams",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = ams_i2c_attach,
+ .detach_adapter = ams_i2c_detach,
+};
+
+static s32 ams_i2c_read(u8 reg)
+{
+ return i2c_smbus_read_byte_data(&ams.i2c_client, reg);
+}
+
+static int ams_i2c_write(u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(&ams.i2c_client, reg, value);
+}
+
+static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
+{
+ s32 result;
+ int i;
+
+ ams_i2c_write(AMS_COMMAND, cmd);
+ for (i = 0; i < 10; i++) {
+ mdelay(5);
+ result = ams_i2c_read(AMS_COMMAND);
+ if (result == 0 || result & 0x80)
+ return 0;
+ }
+ return -1;
+}
+
+static void ams_i2c_set_irq(enum ams_irq reg, char enable)
+{
+ if (reg & AMS_IRQ_FREEFALL) {
+ u8 val = ams_i2c_read(AMS_CTRLX);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_i2c_write(AMS_CTRLX, val);
+ }
+
+ if (reg & AMS_IRQ_SHOCK) {
+ u8 val = ams_i2c_read(AMS_CTRLY);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_i2c_write(AMS_CTRLY, val);
+ }
+
+ if (reg & AMS_IRQ_GLOBAL) {
+ u8 val = ams_i2c_read(AMS_CTRLZ);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_i2c_write(AMS_CTRLZ, val);
+ }
+}
+
+static void ams_i2c_clear_irq(enum ams_irq reg)
+{
+ if (reg & AMS_IRQ_FREEFALL)
+ ams_i2c_write(AMS_FREEFALL, 0);
+
+ if (reg & AMS_IRQ_SHOCK)
+ ams_i2c_write(AMS_SHOCK, 0);
+}
+
+static u8 ams_i2c_get_vendor(void)
+{
+ return ams_i2c_read(AMS_VENDOR);
+}
+
+static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
+{
+ *x = ams_i2c_read(AMS_DATAX);
+ *y = ams_i2c_read(AMS_DATAY);
+ *z = ams_i2c_read(AMS_DATAZ);
+}
+
+static int ams_i2c_attach(struct i2c_adapter *adapter)
+{
+ unsigned long bus;
+ int vmaj, vmin;
+ int result;
+
+ if (strncmp(adapter->name, "uni-n", 5))
+ return -ENODEV;
+
+ bus = simple_strtoul(adapter->name + 6, NULL, 10);
+ if (bus != ams.i2c_bus)
+ return -ENODEV;
+
+ ams.i2c_client.addr = ams.i2c_address;
+ ams.i2c_client.adapter = adapter;
+ ams.i2c_client.driver = &ams_i2c_driver;
+ strcpy(ams.i2c_client.name, "Apple Motion Sensor");
+
+ if (ams_i2c_cmd(AMS_CMD_RESET)) {
+ printk(KERN_INFO "ams: Failed to reset the device\n");
+ return -ENODEV;
+ }
+
+ if (ams_i2c_cmd(AMS_CMD_START)) {
+ printk(KERN_INFO "ams: Failed to start the device\n");
+ return -ENODEV;
+ }
+
+ /* get version/vendor information */
+ ams_i2c_write(AMS_CTRL1, 0x02);
+ ams_i2c_write(AMS_CTRL2, 0x85);
+ ams_i2c_write(AMS_CTRL3, 0x01);
+
+ ams_i2c_cmd(AMS_CMD_READMEM);
+
+ vmaj = ams_i2c_read(AMS_DATA1);
+ vmin = ams_i2c_read(AMS_DATA2);
+ if (vmaj != 1 || vmin != 52) {
+ printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
+ vmaj, vmin);
+ return -ENODEV;
+ }
+
+ ams_i2c_cmd(AMS_CMD_VERSION);
+
+ vmaj = ams_i2c_read(AMS_DATA1);
+ vmin = ams_i2c_read(AMS_DATA2);
+ if (vmaj != 0 || vmin != 1) {
+ printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
+ vmaj, vmin);
+ return -ENODEV;
+ }
+
+ /* Disable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 0);
+
+ result = ams_sensor_attach();
+ if (result < 0)
+ return result;
+
+ /* Set default values */
+ ams_i2c_write(AMS_SENSLOW, 0x15);
+ ams_i2c_write(AMS_SENSHIGH, 0x60);
+ ams_i2c_write(AMS_CTRLX, 0x08);
+ ams_i2c_write(AMS_CTRLY, 0x0F);
+ ams_i2c_write(AMS_CTRLZ, 0x4F);
+ ams_i2c_write(AMS_UNKNOWN1, 0x14);
+
+ /* Clear interrupts */
+ ams_i2c_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 1;
+
+ /* Enable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 1);
+
+ printk(KERN_INFO "ams: Found I2C based motion sensor\n");
+
+ return 0;
+}
+
+static int ams_i2c_detach(struct i2c_adapter *adapter)
+{
+ i2c_detach_client(&ams.i2c_client);
+
+ /* Disable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_i2c_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 0;
+
+ printk(KERN_INFO "ams: Unloading\n");
+
+ return 0;
+}
+
+static void ams_i2c_exit(void)
+{
+ i2c_del_driver(&ams_i2c_driver);
+}
+
+int __init ams_i2c_init(struct device_node *np)
+{
+ char *tmp_bus;
+ int result;
+ u32 *prop;
+
+ mutex_lock(&ams.lock);
+
+ /* Set implementation stuff */
+ ams.of_node = np;
+ ams.exit = ams_i2c_exit;
+ ams.get_vendor = ams_i2c_get_vendor;
+ ams.get_xyz = ams_i2c_get_xyz;
+ ams.clear_irq = ams_i2c_clear_irq;
+
+ /* look for bus either by path or using "reg" */
+ prop = (u32*)get_property(ams.of_node, "reg", NULL);
+ if (!prop) {
+ result = -ENODEV;
+
+ goto exit;
+ }
+
+ tmp_bus = strstr(ams.of_node->full_name, "/i2c-bus@");
+ if (tmp_bus)
+ ams.i2c_bus = *(tmp_bus + 9) - '0';
+ else
+ ams.i2c_bus = ((*prop) >> 8) & 0x0f;
+ ams.i2c_address = ((*prop) & 0xff) >> 1;
+
+ result = i2c_add_driver(&ams_i2c_driver);
+
+exit:
+ mutex_unlock(&ams.lock);
+
+ return result;
+}
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/ams-mouse.c linux-2.6.17-git20/drivers/hwmon/ams-mouse.c
--- linux-2.6.17-git20.orig/drivers/hwmon/ams-mouse.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git20/drivers/hwmon/ams-mouse.c 2006-07-02 21:57:32.000000000 +0200
@@ -0,0 +1,146 @@
+/*
+ * Apple Motion Sensor driver (mouse emulation)
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "ams.h"
+
+static unsigned int mouse;
+module_param(mouse, bool, 0644);
+MODULE_PARM_DESC(mouse, "Enable the input class device on module load");
+
+static int ams_mouse_kthread(void *data)
+{
+ s8 x, y, z;
+
+ while (!kthread_should_stop()) {
+ mutex_lock(&ams.lock);
+
+ 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_sync(ams.idev);
+
+ mutex_unlock(&ams.lock);
+
+ msleep(25);
+ }
+
+ return 0;
+}
+
+/* Call with lock held! */
+static void ams_mouse_enable(void)
+{
+ s8 x, y, z;
+
+ if (ams.idev)
+ return;
+
+ ams_sensors(&x, &y, &z);
+ ams.xcalib = x;
+ ams.ycalib = y;
+
+ ams.idev = input_allocate_device();
+ if (!ams.idev)
+ return;
+
+ 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);
+
+ 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;
+ return;
+ }
+
+ ams.kthread = kthread_run(ams_mouse_kthread, NULL, "kams");
+ if (IS_ERR(ams.kthread)) {
+ input_unregister_device(ams.idev);
+ ams.idev = NULL;
+ return;
+ }
+}
+
+/* Call with lock held! */
+static void ams_mouse_disable(void)
+{
+ if (ams.idev) {
+ kthread_stop(ams.kthread);
+ input_unregister_device(ams.idev);
+ ams.idev = NULL;
+ }
+}
+
+static ssize_t ams_mouse_show_mouse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", mouse);
+}
+
+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;
+
+ if (oldmouse != mouse) {
+ mutex_lock(&ams.lock);
+
+ if (mouse)
+ ams_mouse_enable();
+ else
+ ams_mouse_disable();
+
+ mutex_unlock(&ams.lock);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+ ams_mouse_show_mouse, ams_mouse_store_mouse);
+
+/* Call with lock held! */
+void ams_mouse_init()
+{
+ device_create_file(&ams.of_dev->dev, &dev_attr_mouse);
+
+ if (mouse)
+ ams_mouse_enable();
+}
+
+/* Call with lock held! */
+void ams_mouse_exit()
+{
+ ams_mouse_disable();
+ device_remove_file(&ams.of_dev->dev, &dev_attr_mouse);
+}
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/ams-pmu.c linux-2.6.17-git20/drivers/hwmon/ams-pmu.c
--- linux-2.6.17-git20.orig/drivers/hwmon/ams-pmu.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git20/drivers/hwmon/ams-pmu.c 2006-07-02 21:57:32.000000000 +0200
@@ -0,0 +1,202 @@
+/*
+ * Apple Motion Sensor driver (PMU variant)
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include "ams.h"
+
+/* Attitude */
+#define AMS_X 0x00
+#define AMS_Y 0x01
+#define AMS_Z 0x02
+
+/* Not exactly known, maybe chip vendor */
+#define AMS_VENDOR 0x03
+
+/* Freefall registers */
+#define AMS_FF_CLEAR 0x04
+#define AMS_FF_ENABLE 0x05
+#define AMS_FF_LOW_LIMIT 0x06
+#define AMS_FF_DEBOUNCE 0x07
+
+/* Shock registers */
+#define AMS_SHOCK_CLEAR 0x08
+#define AMS_SHOCK_ENABLE 0x09
+#define AMS_SHOCK_HIGH_LIMIT 0x0a
+#define AMS_SHOCK_DEBOUNCE 0x0b
+
+/* Global interrupt and power control register */
+#define AMS_CONTROL 0x0c
+
+static u8 ams_pmu_cmd;
+
+static void ams_pmu_req_complete(struct adb_request *req)
+{
+ complete((struct completion *)req->arg);
+}
+
+/* Only call this function from task context */
+static void ams_pmu_set_register(u8 reg, u8 value)
+{
+ static struct adb_request req;
+ DECLARE_COMPLETION(req_complete);
+
+ req.arg = &req_complete;
+ if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value))
+ return;
+
+ wait_for_completion(&req_complete);
+}
+
+/* Only call this function from task context */
+static u8 ams_pmu_get_register(u8 reg)
+{
+ static struct adb_request req;
+ DECLARE_COMPLETION(req_complete);
+
+ req.arg = &req_complete;
+ if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg))
+ return 0;
+
+ wait_for_completion(&req_complete);
+
+ if (req.reply_len > 0)
+ return req.reply[0];
+ else
+ return 0;
+}
+
+/* Enables or disables the specified interrupts */
+static void ams_pmu_set_irq(enum ams_irq reg, char enable)
+{
+ if (reg & AMS_IRQ_FREEFALL) {
+ u8 val = ams_pmu_get_register(AMS_FF_ENABLE);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_pmu_set_register(AMS_FF_ENABLE, val);
+ }
+
+ if (reg & AMS_IRQ_SHOCK) {
+ u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_pmu_set_register(AMS_SHOCK_ENABLE, val);
+ }
+
+ if (reg & AMS_IRQ_GLOBAL) {
+ u8 val = ams_pmu_get_register(AMS_CONTROL);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_pmu_set_register(AMS_CONTROL, val);
+ }
+}
+
+static void ams_pmu_clear_irq(enum ams_irq reg)
+{
+ if (reg & AMS_IRQ_FREEFALL)
+ ams_pmu_set_register(AMS_FF_CLEAR, 0x00);
+
+ if (reg & AMS_IRQ_SHOCK)
+ ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00);
+}
+
+static u8 ams_pmu_get_vendor(void)
+{
+ return ams_pmu_get_register(AMS_VENDOR);
+}
+
+static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
+{
+ *x = ams_pmu_get_register(AMS_X);
+ *y = ams_pmu_get_register(AMS_Y);
+ *z = ams_pmu_get_register(AMS_Z);
+}
+
+static void ams_pmu_exit(void)
+{
+ /* Disable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 0;
+
+ printk(KERN_INFO "ams: Unloading\n");
+}
+
+/* Call with lock held! */
+int __init ams_pmu_init(struct device_node *np)
+{
+ u32 *prop;
+ int result;
+
+ mutex_lock(&ams.lock);
+
+ /* Set implementation stuff */
+ ams.of_node = np;
+ ams.exit = ams_pmu_exit;
+ ams.get_vendor = ams_pmu_get_vendor;
+ ams.get_xyz = ams_pmu_get_xyz;
+ ams.clear_irq = ams_pmu_clear_irq;
+
+ /* Get PMU command, should be 0x4e, but we can never know */
+ prop = (u32*)get_property(ams.of_node, "reg", NULL);
+ if (!prop) {
+ result = -ENODEV;
+ goto exit;
+ }
+ ams_pmu_cmd = ((*prop) >> 8) & 0xff;
+
+ /* Disable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ result = ams_sensor_attach();
+ if (result < 0) {
+ goto exit;
+ }
+
+ /* Set default values */
+ ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
+ ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
+ ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
+
+ ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
+ ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
+ ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
+
+ ams_pmu_set_register(AMS_CONTROL, 0x4f);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 1;
+
+ /* Enable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 1);
+
+ printk(KERN_INFO "ams: Found PMU based motion sensor\n");
+
+ result = 0;
+
+exit:
+ mutex_unlock(&ams.lock);
+
+ return result;
+}
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Kconfig linux-2.6.17-git20/drivers/hwmon/Kconfig
--- linux-2.6.17-git20.orig/drivers/hwmon/Kconfig 2006-07-02 21:49:19.000000000 +0200
+++ linux-2.6.17-git20/drivers/hwmon/Kconfig 2006-07-02 22:20:22.000000000 +0200
@@ -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
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Makefile linux-2.6.17-git20/drivers/hwmon/Makefile
--- linux-2.6.17-git20.orig/drivers/hwmon/Makefile 2006-07-02 21:49:19.000000000 +0200
+++ linux-2.6.17-git20/drivers/hwmon/Makefile 2006-07-02 22:17:43.000000000 +0200
@@ -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
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
@ 2006-07-03 2:33 ` Dmitry Torokhov
2006-07-03 3:14 ` Andrew Morton
` (4 subsequent siblings)
5 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2006-07-03 2:33 UTC (permalink / raw)
To: Michael Hanselmann
Cc: linux-kernel, lm-sensors, khali, linux-kernel, benh, johannes,
stelian, chainsaw
Hi Michael,
On Sunday 02 July 2006 18:26, Michael Hanselmann wrote:
>
> +
> + 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?
> +
> + 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?
> + 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.
--
Dmitry
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
2006-07-03 2:33 ` Dmitry Torokhov
@ 2006-07-03 3:14 ` Andrew Morton
2006-07-03 3:47 ` Benjamin Herrenschmidt
2006-07-03 6:56 ` Michael Hanselmann
2006-07-03 7:29 ` Jean Delvare
` (3 subsequent siblings)
5 siblings, 2 replies; 19+ messages in thread
From: Andrew Morton @ 2006-07-03 3:14 UTC (permalink / raw)
To: Michael Hanselmann
Cc: linux-kernel, lm-sensors, khali, linux-kernel, benh, johannes,
stelian, chainsaw
On Mon, 3 Jul 2006 00:26:49 +0200
Michael Hanselmann <linux-kernel@hansmi.ch> wrote:
> 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@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@popies.net>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Dmitry Torokhov <dtor_core@ameritech.net>
Cc: Robert Love <rml@novell.com>
Cc: Jean Delvare <khali@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
--- /dev/null
+++ a/drivers/hwmon/ams.c
@@ -0,0 +1,544 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ *
+ * Clean room implementation based on the reverse engineered OSX driver by
+ * Johannes Berg <johannes@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
--- a/drivers/hwmon/Kconfig~apple-motion-sensor-driver
+++ a/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
diff -puN drivers/hwmon/Makefile~apple-motion-sensor-driver drivers/hwmon/Makefile
--- a/drivers/hwmon/Makefile~apple-motion-sensor-driver
+++ a/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
_
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 3:14 ` Andrew Morton
@ 2006-07-03 3:47 ` Benjamin Herrenschmidt
2006-07-03 4:00 ` Andrew Morton
2006-07-03 6:56 ` Michael Hanselmann
1 sibling, 1 reply; 19+ messages in thread
From: Benjamin Herrenschmidt @ 2006-07-03 3:47 UTC (permalink / raw)
To: Andrew Morton
Cc: Michael Hanselmann, linux-kernel, lm-sensors, khali, linux-kernel,
johannes, stelian, chainsaw
On Sun, 2006-07-02 at 20:14 -0700, Andrew Morton wrote:
> On Mon, 3 Jul 2006 00:26:49 +0200
> Michael Hanselmann <linux-kernel@hansmi.ch> wrote:
>
> > 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.
> From: Stelian Pop <stelian@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@popies.net>
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Paul Mackerras <paulus@samba.org>
> Cc: Dmitry Torokhov <dtor_core@ameritech.net>
> Cc: Robert Love <rml@novell.com>
> Cc: Jean Delvare <khali@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
> --- /dev/null
> +++ a/drivers/hwmon/ams.c
> @@ -0,0 +1,544 @@
> +/*
> + * Apple Motion Sensor driver
> + *
> + * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
> + *
> + * Clean room implementation based on the reverse engineered OSX driver by
> + * Johannes Berg <johannes@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
> --- a/drivers/hwmon/Kconfig~apple-motion-sensor-driver
> +++ a/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
> diff -puN drivers/hwmon/Makefile~apple-motion-sensor-driver drivers/hwmon/Makefile
> --- a/drivers/hwmon/Makefile~apple-motion-sensor-driver
> +++ a/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
> _
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 3:47 ` Benjamin Herrenschmidt
@ 2006-07-03 4:00 ` Andrew Morton
0 siblings, 0 replies; 19+ messages in thread
From: Andrew Morton @ 2006-07-03 4:00 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linux-kernel, linux-kernel, lm-sensors, khali, linux-kernel,
johannes, stelian, chainsaw
On Mon, 03 Jul 2006 13:47:28 +1000
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> On Sun, 2006-07-02 at 20:14 -0700, Andrew Morton wrote:
> > On Mon, 3 Jul 2006 00:26:49 +0200
> > Michael Hanselmann <linux-kernel@hansmi.ch> wrote:
> >
> > > 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).
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.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 3:14 ` Andrew Morton
2006-07-03 3:47 ` Benjamin Herrenschmidt
@ 2006-07-03 6:56 ` Michael Hanselmann
2006-07-03 9:02 ` Stelian Pop
1 sibling, 1 reply; 19+ messages in thread
From: Michael Hanselmann @ 2006-07-03 6:56 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, lm-sensors, khali, linux-kernel, benh, johannes,
stelian, chainsaw
Hello Andrew
On Sun, Jul 02, 2006 at 08:14:15PM -0700, Andrew Morton wrote:
> On Mon, 3 Jul 2006 00:26:49 +0200
> Michael Hanselmann <linux-kernel@hansmi.ch> wrote:
> > 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@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
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
2006-07-03 2:33 ` Dmitry Torokhov
2006-07-03 3:14 ` Andrew Morton
@ 2006-07-03 7:29 ` Jean Delvare
2006-07-03 8:52 ` Benjamin Herrenschmidt
2006-07-03 10:12 ` Stelian Pop
` (2 subsequent siblings)
5 siblings, 1 reply; 19+ messages in thread
From: Jean Delvare @ 2006-07-03 7:29 UTC (permalink / raw)
To: Michael Hanselmann
Cc: linux-kernel, linux-kernel, benh, johannes, stelian, chainsaw
Hi Michael,
> 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'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.
> 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.
> diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Kconfig linux-2.6.17-git20/drivers/hwmon/Kconfig
> --- linux-2.6.17-git20.orig/drivers/hwmon/Kconfig 2006-07-02 21:49:19.000000000 +0200
> +++ linux-2.6.17-git20/drivers/hwmon/Kconfig 2006-07-02 22:20:22.000000000 +0200
> @@ -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.
> + 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
> --- linux-2.6.17-git20.orig/drivers/hwmon/Makefile 2006-07-02 21:49:19.000000000 +0200
> +++ linux-2.6.17-git20/drivers/hwmon/Makefile 2006-07-02 22:17:43.000000000 +0200
> @@ -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,
--
Jean Delvare
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 7:29 ` Jean Delvare
@ 2006-07-03 8:52 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 19+ messages in thread
From: Benjamin Herrenschmidt @ 2006-07-03 8:52 UTC (permalink / raw)
To: Jean Delvare
Cc: Michael Hanselmann, linux-kernel, linux-kernel, johannes, stelian,
chainsaw
> 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.
:)
> > 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.
> > +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)
> 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.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 6:56 ` Michael Hanselmann
@ 2006-07-03 9:02 ` Stelian Pop
0 siblings, 0 replies; 19+ messages in thread
From: Stelian Pop @ 2006-07-03 9:02 UTC (permalink / raw)
To: Michael Hanselmann
Cc: Andrew Morton, linux-kernel, lm-sensors, khali, linux-kernel,
benh, johannes, chainsaw
Le lundi 03 juillet 2006 à 08:56 +0200, Michael Hanselmann a écrit :
> Hello Andrew
>
> On Sun, Jul 02, 2006 at 08:14:15PM -0700, Andrew Morton wrote:
> > On Mon, 3 Jul 2006 00:26:49 +0200
> > Michael Hanselmann <linux-kernel@hansmi.ch> wrote:
>
> > > 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@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.
I have no problems with this approach. I'll take a look at your patch
ASAP.
Stelian.
--
Stelian Pop <stelian@popies.net>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
` (2 preceding siblings ...)
2006-07-03 7:29 ` Jean Delvare
@ 2006-07-03 10:12 ` Stelian Pop
2006-07-03 10:19 ` Johannes Berg
2006-07-03 10:45 ` Michael Hanselmann
2006-07-03 13:44 ` Dmitry Torokhov
2006-07-03 22:45 ` Michael Hanselmann
5 siblings, 2 replies; 19+ messages in thread
From: Stelian Pop @ 2006-07-03 10:12 UTC (permalink / raw)
To: Michael Hanselmann
Cc: linux-kernel, lm-sensors, khali, linux-kernel, benh, johannes,
chainsaw
Le lundi 03 juillet 2006 à 00:26 +0200, Michael Hanselmann a écrit :
> [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:
> + /* calibrated null values */
> + int xcalib, ycalib;
Add zcalib too for movement on z-axis.
> + input_report_abs(ams.idev, ABS_X, x - ams.xcalib);
> + input_report_abs(ams.idev, ABS_Y, y - ams.ycalib);
Ditto, ABS_Z...
> + 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...
> +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;
> +
> + 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.
> +
> +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...
> + result = ams_sensor_attach();
> + if (result < 0) {
> + goto exit;
> + }
No need for braces.
> +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.
--
Stelian Pop <stelian@popies.net>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 10:12 ` Stelian Pop
@ 2006-07-03 10:19 ` Johannes Berg
2006-07-03 10:45 ` Michael Hanselmann
1 sibling, 0 replies; 19+ messages in thread
From: Johannes Berg @ 2006-07-03 10:19 UTC (permalink / raw)
To: Stelian Pop
Cc: Michael Hanselmann, linux-kernel, lm-sensors, khali, linux-kernel,
benh, chainsaw
[-- Attachment #1: Type: text/plain, Size: 256 bytes --]
> 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
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 10:12 ` Stelian Pop
2006-07-03 10:19 ` Johannes Berg
@ 2006-07-03 10:45 ` Michael Hanselmann
2006-07-03 10:49 ` Benjamin Herrenschmidt
2006-07-03 13:25 ` Stefan Richter
1 sibling, 2 replies; 19+ messages in thread
From: Michael Hanselmann @ 2006-07-03 10:45 UTC (permalink / raw)
To: Stelian Pop
Cc: linux-kernel, lm-sensors, khali, linux-kernel, benh, johannes,
chainsaw
On Mon, Jul 03, 2006 at 12:12:47PM +0200, Stelian Pop wrote:
> > +
> > +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)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 10:45 ` Michael Hanselmann
@ 2006-07-03 10:49 ` Benjamin Herrenschmidt
2006-07-03 11:31 ` Stelian Pop
2006-07-03 13:25 ` Stefan Richter
1 sibling, 1 reply; 19+ messages in thread
From: Benjamin Herrenschmidt @ 2006-07-03 10:49 UTC (permalink / raw)
To: Michael Hanselmann
Cc: Stelian Pop, linux-kernel, lm-sensors, khali, linux-kernel,
johannes, chainsaw
On Mon, 2006-07-03 at 12:45 +0200, Michael Hanselmann wrote:
> On Mon, Jul 03, 2006 at 12:12:47PM +0200, Stelian Pop wrote:
> > > +
> > > +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?
Cache the values and only re-read from the hardware after a given amount
of time has elapsed ?
> Thanks,
> Michael
>
> (All other issues, including those in the other mails, are clear and
> will be addressed by a new patch)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 10:49 ` Benjamin Herrenschmidt
@ 2006-07-03 11:31 ` Stelian Pop
2006-07-03 13:16 ` Dmitry Torokhov
0 siblings, 1 reply; 19+ messages in thread
From: Stelian Pop @ 2006-07-03 11:31 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Michael Hanselmann, linux-kernel, lm-sensors, khali, linux-kernel,
johannes, chainsaw
Le lundi 03 juillet 2006 à 20:49 +1000, Benjamin Herrenschmidt a écrit :
> On Mon, 2006-07-03 at 12:45 +0200, Michael Hanselmann wrote:
> > On Mon, Jul 03, 2006 at 12:12:47PM +0200, Stelian Pop wrote:
> > > > +
> > > > +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?
>
> 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.
--
Stelian Pop <stelian@popies.net>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 11:31 ` Stelian Pop
@ 2006-07-03 13:16 ` Dmitry Torokhov
0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2006-07-03 13:16 UTC (permalink / raw)
To: Stelian Pop
Cc: Benjamin Herrenschmidt, Michael Hanselmann, linux-kernel,
lm-sensors, khali, linux-kernel, johannes, chainsaw
On Monday 03 July 2006 07:31, Stelian Pop wrote:
> Le lundi 03 juillet 2006 à 20:49 +1000, Benjamin Herrenschmidt a écrit :
> > On Mon, 2006-07-03 at 12:45 +0200, Michael Hanselmann wrote:
> > > On Mon, Jul 03, 2006 at 12:12:47PM +0200, Stelian Pop wrote:
> > > > > +
> > > > > +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?
> >
> > 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.
--
Dmitry
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 10:45 ` Michael Hanselmann
2006-07-03 10:49 ` Benjamin Herrenschmidt
@ 2006-07-03 13:25 ` Stefan Richter
1 sibling, 0 replies; 19+ messages in thread
From: Stefan Richter @ 2006-07-03 13:25 UTC (permalink / raw)
To: Michael Hanselmann
Cc: Stelian Pop, linux-kernel, lm-sensors, khali, linux-kernel, benh,
johannes, chainsaw
Michael Hanselmann wrote:
> On Mon, Jul 03, 2006 at 12:12:47PM +0200, Stelian Pop wrote:
>> > +
>> > +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.]
>> 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.
--
Stefan Richter
-=====-=-==- -==- ===--
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
` (3 preceding siblings ...)
2006-07-03 10:12 ` Stelian Pop
@ 2006-07-03 13:44 ` Dmitry Torokhov
2006-07-03 22:45 ` Michael Hanselmann
5 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2006-07-03 13:44 UTC (permalink / raw)
To: Michael Hanselmann
Cc: linux-kernel, lm-sensors, khali, linux-kernel, benh, johannes,
stelian, chainsaw
On Sunday 02 July 2006 18:26, Michael Hanselmann wrote:
> + 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.
--
Dmitry
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
` (4 preceding siblings ...)
2006-07-03 13:44 ` Dmitry Torokhov
@ 2006-07-03 22:45 ` Michael Hanselmann
2006-07-04 16:45 ` Michael Buesch
5 siblings, 1 reply; 19+ messages in thread
From: Michael Hanselmann @ 2006-07-03 22:45 UTC (permalink / raw)
To: linux-kernel
Cc: lm-sensors, khali, linux-kernel, benh, johannes, stelian,
chainsaw, dtor, stefanr
Hello
On Mon, Jul 03, 2006 at 12:26:49AM +0200, Michael Hanselmann wrote:
> 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
---
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
--- linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-core.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git21/drivers/hwmon/ams/ams-core.c 2006-07-04 00:41:53.000000000 +0200
@@ -0,0 +1,242 @@
+/*
+ * Apple Motion Sensor driver
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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);
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/ams/ams.h linux-2.6.17-git21/drivers/hwmon/ams/ams.h
--- linux-2.6.17-git21.orig/drivers/hwmon/ams/ams.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git21/drivers/hwmon/ams/ams.h 2006-07-04 00:41:33.000000000 +0200
@@ -0,0 +1,72 @@
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <asm/of_device.h>
+
+enum ams_irq {
+ AMS_IRQ_FREEFALL = 0x01,
+ AMS_IRQ_SHOCK = 0x02,
+ AMS_IRQ_GLOBAL = 0x04,
+ AMS_IRQ_ALL =
+ AMS_IRQ_FREEFALL |
+ AMS_IRQ_SHOCK |
+ AMS_IRQ_GLOBAL,
+};
+
+struct ams {
+ /* Locks */
+ spinlock_t irq_lock;
+ struct mutex lock;
+
+ /* General properties */
+ struct device_node *of_node;
+ struct of_device *of_dev;
+ char has_device;
+ char vflag;
+ u32 orient1;
+ u32 orient2;
+
+ /* Interrupt worker */
+ struct work_struct worker;
+ u8 worker_irqs;
+
+ /* Implementation
+ *
+ * Only call these functions with the main lock held.
+ */
+ void (*exit)(void);
+
+ void (*get_xyz)(s8 *x, s8 *y, s8 *z);
+ u8 (*get_vendor)(void);
+
+ void (*clear_irq)(enum ams_irq reg);
+
+#ifdef CONFIG_SENSORS_AMS_I2C
+ /* I2C properties */
+ int i2c_bus;
+ int i2c_address;
+ struct i2c_client i2c_client;
+#endif
+
+ /* Mouse emulation */
+ struct task_struct *kthread;
+ struct input_dev *idev;
+ __u16 bustype;
+
+ /* calibrated null values */
+ int xcalib, ycalib, zcalib;
+};
+
+extern struct ams ams;
+
+extern void ams_sensors(s8 *x, s8 *y, s8 *z);
+extern int ams_sensor_attach(void);
+
+extern int ams_pmu_init(struct device_node *np);
+extern int ams_i2c_init(struct device_node *np);
+
+extern void ams_mouse_init(void);
+extern void ams_mouse_exit(void);
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-i2c.c linux-2.6.17-git21/drivers/hwmon/ams/ams-i2c.c
--- linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-i2c.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git21/drivers/hwmon/ams/ams-i2c.c 2006-07-04 00:05:17.000000000 +0200
@@ -0,0 +1,289 @@
+/*
+ * Apple Motion Sensor driver (I2C variant)
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * Clean room implementation based on the reverse engineered Mac OS X driver by
+ * Johannes Berg <johannes@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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "ams.h"
+
+/* 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_i2c_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,
+};
+
+static int ams_i2c_attach(struct i2c_adapter *adapter);
+static int ams_i2c_detach(struct i2c_adapter *adapter);
+
+static struct i2c_driver ams_i2c_driver = {
+ .driver = {
+ .name = "ams",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = ams_i2c_attach,
+ .detach_adapter = ams_i2c_detach,
+};
+
+static s32 ams_i2c_read(u8 reg)
+{
+ return i2c_smbus_read_byte_data(&ams.i2c_client, reg);
+}
+
+static int ams_i2c_write(u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(&ams.i2c_client, reg, value);
+}
+
+static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
+{
+ s32 result;
+ int i;
+
+ ams_i2c_write(AMS_COMMAND, cmd);
+ for (i = 0; i < 10; i++) {
+ mdelay(5);
+ result = ams_i2c_read(AMS_COMMAND);
+ if (result == 0 || result & 0x80)
+ return 0;
+ }
+ return -1;
+}
+
+static void ams_i2c_set_irq(enum ams_irq reg, char enable)
+{
+ if (reg & AMS_IRQ_FREEFALL) {
+ u8 val = ams_i2c_read(AMS_CTRLX);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_i2c_write(AMS_CTRLX, val);
+ }
+
+ if (reg & AMS_IRQ_SHOCK) {
+ u8 val = ams_i2c_read(AMS_CTRLY);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_i2c_write(AMS_CTRLY, val);
+ }
+
+ if (reg & AMS_IRQ_GLOBAL) {
+ u8 val = ams_i2c_read(AMS_CTRLZ);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_i2c_write(AMS_CTRLZ, val);
+ }
+}
+
+static void ams_i2c_clear_irq(enum ams_irq reg)
+{
+ if (reg & AMS_IRQ_FREEFALL)
+ ams_i2c_write(AMS_FREEFALL, 0);
+
+ if (reg & AMS_IRQ_SHOCK)
+ ams_i2c_write(AMS_SHOCK, 0);
+}
+
+static u8 ams_i2c_get_vendor(void)
+{
+ return ams_i2c_read(AMS_VENDOR);
+}
+
+static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
+{
+ *x = ams_i2c_read(AMS_DATAX);
+ *y = ams_i2c_read(AMS_DATAY);
+ *z = ams_i2c_read(AMS_DATAZ);
+}
+
+static int ams_i2c_attach(struct i2c_adapter *adapter)
+{
+ unsigned long bus;
+ int vmaj, vmin;
+ int result;
+
+ /* There can be only one */
+ if (unlikely(ams.has_device))
+ return -ENODEV;
+
+ if (strncmp(adapter->name, "uni-n", 5))
+ return -ENODEV;
+
+ bus = simple_strtoul(adapter->name + 6, NULL, 10);
+ if (bus != ams.i2c_bus)
+ return -ENODEV;
+
+ ams.i2c_client.addr = ams.i2c_address;
+ ams.i2c_client.adapter = adapter;
+ ams.i2c_client.driver = &ams_i2c_driver;
+ strcpy(ams.i2c_client.name, "Apple Motion Sensor");
+
+ if (ams_i2c_cmd(AMS_CMD_RESET)) {
+ printk(KERN_INFO "ams: Failed to reset the device\n");
+ return -ENODEV;
+ }
+
+ if (ams_i2c_cmd(AMS_CMD_START)) {
+ printk(KERN_INFO "ams: Failed to start the device\n");
+ return -ENODEV;
+ }
+
+ /* get version/vendor information */
+ ams_i2c_write(AMS_CTRL1, 0x02);
+ ams_i2c_write(AMS_CTRL2, 0x85);
+ ams_i2c_write(AMS_CTRL3, 0x01);
+
+ ams_i2c_cmd(AMS_CMD_READMEM);
+
+ vmaj = ams_i2c_read(AMS_DATA1);
+ vmin = ams_i2c_read(AMS_DATA2);
+ if (vmaj != 1 || vmin != 52) {
+ printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
+ vmaj, vmin);
+ return -ENODEV;
+ }
+
+ ams_i2c_cmd(AMS_CMD_VERSION);
+
+ vmaj = ams_i2c_read(AMS_DATA1);
+ vmin = ams_i2c_read(AMS_DATA2);
+ if (vmaj != 0 || vmin != 1) {
+ printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
+ vmaj, vmin);
+ return -ENODEV;
+ }
+
+ /* Disable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 0);
+
+ result = ams_sensor_attach();
+ if (result < 0)
+ return result;
+
+ /* Set default values */
+ ams_i2c_write(AMS_SENSLOW, 0x15);
+ ams_i2c_write(AMS_SENSHIGH, 0x60);
+ ams_i2c_write(AMS_CTRLX, 0x08);
+ ams_i2c_write(AMS_CTRLY, 0x0F);
+ ams_i2c_write(AMS_CTRLZ, 0x4F);
+ ams_i2c_write(AMS_UNKNOWN1, 0x14);
+
+ /* Clear interrupts */
+ ams_i2c_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 1;
+
+ /* Enable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 1);
+
+ printk(KERN_INFO "ams: Found I2C based motion sensor\n");
+
+ return 0;
+}
+
+static int ams_i2c_detach(struct i2c_adapter *adapter)
+{
+ /* Disable interrupts */
+ ams_i2c_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_i2c_clear_irq(AMS_IRQ_ALL);
+
+ i2c_detach_client(&ams.i2c_client);
+
+ ams.has_device = 0;
+
+ printk(KERN_INFO "ams: Unloading\n");
+
+ return 0;
+}
+
+static void ams_i2c_exit(void)
+{
+ i2c_del_driver(&ams_i2c_driver);
+}
+
+int __init ams_i2c_init(struct device_node *np)
+{
+ char *tmp_bus;
+ int result;
+ u32 *prop;
+
+ mutex_lock(&ams.lock);
+
+ /* Set implementation stuff */
+ ams.of_node = np;
+ ams.exit = ams_i2c_exit;
+ ams.get_vendor = ams_i2c_get_vendor;
+ ams.get_xyz = ams_i2c_get_xyz;
+ ams.clear_irq = ams_i2c_clear_irq;
+ ams.bustype = BUS_I2C;
+
+ /* look for bus either by path or using "reg" */
+ prop = (u32*)get_property(ams.of_node, "reg", NULL);
+ if (!prop) {
+ result = -ENODEV;
+
+ goto exit;
+ }
+
+ tmp_bus = strstr(ams.of_node->full_name, "/i2c-bus@");
+ if (tmp_bus)
+ ams.i2c_bus = *(tmp_bus + 9) - '0';
+ else
+ ams.i2c_bus = ((*prop) >> 8) & 0x0f;
+ ams.i2c_address = ((*prop) & 0xff) >> 1;
+
+ result = i2c_add_driver(&ams_i2c_driver);
+
+exit:
+ mutex_unlock(&ams.lock);
+
+ return result;
+}
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-mouse.c linux-2.6.17-git21/drivers/hwmon/ams/ams-mouse.c
--- linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-mouse.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git21/drivers/hwmon/ams/ams-mouse.c 2006-07-03 21:48:07.000000000 +0200
@@ -0,0 +1,145 @@
+/*
+ * Apple Motion Sensor driver (mouse emulation)
+ *
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "ams.h"
+
+static unsigned int mouse;
+module_param(mouse, bool, 0644);
+MODULE_PARM_DESC(mouse, "Enable the input class device on module load");
+
+static int ams_mouse_kthread(void *data)
+{
+ s8 x, y, z;
+
+ while (!kthread_should_stop()) {
+ mutex_lock(&ams.lock);
+
+ 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);
+
+ mutex_unlock(&ams.lock);
+
+ msleep(25);
+ }
+
+ return 0;
+}
+
+/* Call with lock held! */
+static void ams_mouse_enable(void)
+{
+ s8 x, y, z;
+
+ if (ams.idev)
+ return;
+
+ ams_sensors(&x, &y, &z);
+ ams.xcalib = x;
+ ams.ycalib = y;
+ ams.zcalib = z;
+
+ ams.idev = input_allocate_device();
+ if (!ams.idev)
+ return;
+
+ ams.idev->name = "Apple Motion Sensor";
+ ams.idev->id.bustype = ams.bustype;
+ ams.idev->id.vendor = 0;
+ ams.idev->cdev.dev = &ams.of_dev->dev;
+
+ 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);
+
+ if (input_register_device(ams.idev)) {
+ input_free_device(ams.idev);
+ ams.idev = NULL;
+ return;
+ }
+
+ ams.kthread = kthread_run(ams_mouse_kthread, NULL, "kams");
+ if (IS_ERR(ams.kthread)) {
+ input_unregister_device(ams.idev);
+ ams.idev = NULL;
+ return;
+ }
+}
+
+/* Call with lock held! */
+static void ams_mouse_disable(void)
+{
+ if (ams.idev) {
+ kthread_stop(ams.kthread);
+ input_unregister_device(ams.idev);
+ ams.idev = NULL;
+ }
+}
+
+static ssize_t ams_mouse_show_mouse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", mouse);
+}
+
+static ssize_t ams_mouse_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;
+
+ mutex_lock(&ams.lock);
+
+ if (mouse)
+ ams_mouse_enable();
+ else
+ ams_mouse_disable();
+
+ mutex_unlock(&ams.lock);
+
+ return count;
+}
+
+static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR,
+ ams_mouse_show_mouse, ams_mouse_store_mouse);
+
+/* Call with lock held! */
+void ams_mouse_init()
+{
+ device_create_file(&ams.of_dev->dev, &dev_attr_mouse);
+
+ if (mouse)
+ ams_mouse_enable();
+}
+
+/* Call with lock held! */
+void ams_mouse_exit()
+{
+ ams_mouse_disable();
+ device_remove_file(&ams.of_dev->dev, &dev_attr_mouse);
+}
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-pmu.c linux-2.6.17-git21/drivers/hwmon/ams/ams-pmu.c
--- linux-2.6.17-git21.orig/drivers/hwmon/ams/ams-pmu.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git21/drivers/hwmon/ams/ams-pmu.c 2006-07-03 21:45:16.000000000 +0200
@@ -0,0 +1,202 @@
+/*
+ * Apple Motion Sensor driver (PMU variant)
+ *
+ * Copyright (C) 2006 Michael Hanselmann (linux-kernel@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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include "ams.h"
+
+/* Attitude */
+#define AMS_X 0x00
+#define AMS_Y 0x01
+#define AMS_Z 0x02
+
+/* Not exactly known, maybe chip vendor */
+#define AMS_VENDOR 0x03
+
+/* Freefall registers */
+#define AMS_FF_CLEAR 0x04
+#define AMS_FF_ENABLE 0x05
+#define AMS_FF_LOW_LIMIT 0x06
+#define AMS_FF_DEBOUNCE 0x07
+
+/* Shock registers */
+#define AMS_SHOCK_CLEAR 0x08
+#define AMS_SHOCK_ENABLE 0x09
+#define AMS_SHOCK_HIGH_LIMIT 0x0a
+#define AMS_SHOCK_DEBOUNCE 0x0b
+
+/* Global interrupt and power control register */
+#define AMS_CONTROL 0x0c
+
+static u8 ams_pmu_cmd;
+
+static void ams_pmu_req_complete(struct adb_request *req)
+{
+ complete((struct completion *)req->arg);
+}
+
+/* Only call this function from task context */
+static void ams_pmu_set_register(u8 reg, u8 value)
+{
+ static struct adb_request req;
+ DECLARE_COMPLETION(req_complete);
+
+ req.arg = &req_complete;
+ if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value))
+ return;
+
+ wait_for_completion(&req_complete);
+}
+
+/* Only call this function from task context */
+static u8 ams_pmu_get_register(u8 reg)
+{
+ static struct adb_request req;
+ DECLARE_COMPLETION(req_complete);
+
+ req.arg = &req_complete;
+ if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg))
+ return 0;
+
+ wait_for_completion(&req_complete);
+
+ if (req.reply_len > 0)
+ return req.reply[0];
+ else
+ return 0;
+}
+
+/* Enables or disables the specified interrupts */
+static void ams_pmu_set_irq(enum ams_irq reg, char enable)
+{
+ if (reg & AMS_IRQ_FREEFALL) {
+ u8 val = ams_pmu_get_register(AMS_FF_ENABLE);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_pmu_set_register(AMS_FF_ENABLE, val);
+ }
+
+ if (reg & AMS_IRQ_SHOCK) {
+ u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_pmu_set_register(AMS_SHOCK_ENABLE, val);
+ }
+
+ if (reg & AMS_IRQ_GLOBAL) {
+ u8 val = ams_pmu_get_register(AMS_CONTROL);
+ if (enable) val |= 0x80;
+ else val &= ~0x80;
+ ams_pmu_set_register(AMS_CONTROL, val);
+ }
+}
+
+static void ams_pmu_clear_irq(enum ams_irq reg)
+{
+ if (reg & AMS_IRQ_FREEFALL)
+ ams_pmu_set_register(AMS_FF_CLEAR, 0x00);
+
+ if (reg & AMS_IRQ_SHOCK)
+ ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00);
+}
+
+static u8 ams_pmu_get_vendor(void)
+{
+ return ams_pmu_get_register(AMS_VENDOR);
+}
+
+static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
+{
+ *x = ams_pmu_get_register(AMS_X);
+ *y = ams_pmu_get_register(AMS_Y);
+ *z = ams_pmu_get_register(AMS_Z);
+}
+
+static void ams_pmu_exit(void)
+{
+ /* Disable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 0;
+
+ printk(KERN_INFO "ams: Unloading\n");
+}
+
+/* Call with lock held! */
+int __init ams_pmu_init(struct device_node *np)
+{
+ u32 *prop;
+ int result;
+
+ mutex_lock(&ams.lock);
+
+ /* Set implementation stuff */
+ ams.of_node = np;
+ ams.exit = ams_pmu_exit;
+ ams.get_vendor = ams_pmu_get_vendor;
+ ams.get_xyz = ams_pmu_get_xyz;
+ ams.clear_irq = ams_pmu_clear_irq;
+ ams.bustype = BUS_HOST;
+
+ /* Get PMU command, should be 0x4e, but we can never know */
+ prop = (u32*)get_property(ams.of_node, "reg", NULL);
+ if (!prop) {
+ result = -ENODEV;
+ goto exit;
+ }
+ ams_pmu_cmd = ((*prop) >> 8) & 0xff;
+
+ /* Disable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 0);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ result = ams_sensor_attach();
+ if (result < 0)
+ goto exit;
+
+ /* Set default values */
+ ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
+ ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
+ ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
+
+ ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
+ ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
+ ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
+
+ ams_pmu_set_register(AMS_CONTROL, 0x4f);
+
+ /* Clear interrupts */
+ ams_pmu_clear_irq(AMS_IRQ_ALL);
+
+ ams.has_device = 1;
+
+ /* Enable interrupts */
+ ams_pmu_set_irq(AMS_IRQ_ALL, 1);
+
+ printk(KERN_INFO "ams: Found PMU based motion sensor\n");
+
+ result = 0;
+
+exit:
+ mutex_unlock(&ams.lock);
+
+ return result;
+}
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
--- linux-2.6.17-git21.orig/drivers/hwmon/ams/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-git21/drivers/hwmon/ams/Makefile 2006-07-04 00:42:44.000000000 +0200
@@ -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
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/Kconfig linux-2.6.17-git21/drivers/hwmon/Kconfig
--- linux-2.6.17-git21.orig/drivers/hwmon/Kconfig 2006-07-04 00:29:56.000000000 +0200
+++ linux-2.6.17-git21/drivers/hwmon/Kconfig 2006-07-04 00:12:23.000000000 +0200
@@ -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
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git21.orig/drivers/hwmon/Makefile linux-2.6.17-git21/drivers/hwmon/Makefile
--- linux-2.6.17-git21.orig/drivers/hwmon/Makefile 2006-07-04 00:29:56.000000000 +0200
+++ linux-2.6.17-git21/drivers/hwmon/Makefile 2006-07-03 20:15:50.000000000 +0200
@@ -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
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC] Apple Motion Sensor driver
2006-07-03 22:45 ` Michael Hanselmann
@ 2006-07-04 16:45 ` Michael Buesch
0 siblings, 0 replies; 19+ messages in thread
From: Michael Buesch @ 2006-07-04 16:45 UTC (permalink / raw)
To: Michael Hanselmann
Cc: lm-sensors, khali, linux-kernel, benh, johannes, stelian,
chainsaw, dtor, stefanr, linux-kernel
On Tuesday 04 July 2006 00:45, you wrote:
> +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.
> +static void ams_worker(void *data)
> +{
unsigned long flags;
> + mutex_lock(&ams.lock);
> +
> + if (ams.has_device) {
spin_lock_irqsave(&ams.irq_lock, flags);
> + 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);
> +
spin_unlock_irqrestore(&ams.irq_lock, flags);
> + }
> +
> + 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.
--
Greetings Michael.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2006-07-04 16:45 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-02 22:26 [RFC] Apple Motion Sensor driver Michael Hanselmann
2006-07-03 2:33 ` Dmitry Torokhov
2006-07-03 3:14 ` Andrew Morton
2006-07-03 3:47 ` Benjamin Herrenschmidt
2006-07-03 4:00 ` Andrew Morton
2006-07-03 6:56 ` Michael Hanselmann
2006-07-03 9:02 ` Stelian Pop
2006-07-03 7:29 ` Jean Delvare
2006-07-03 8:52 ` Benjamin Herrenschmidt
2006-07-03 10:12 ` Stelian Pop
2006-07-03 10:19 ` Johannes Berg
2006-07-03 10:45 ` Michael Hanselmann
2006-07-03 10:49 ` Benjamin Herrenschmidt
2006-07-03 11:31 ` Stelian Pop
2006-07-03 13:16 ` Dmitry Torokhov
2006-07-03 13:25 ` Stefan Richter
2006-07-03 13:44 ` Dmitry Torokhov
2006-07-03 22:45 ` Michael Hanselmann
2006-07-04 16:45 ` Michael Buesch
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox