On Tue, 2011-08-09 at 12:14 -0400, Matthew Garrett wrote: > The applesmc hardware supports providing interrupts for significant > accelerometer events. Add framework for this - we'll want to notify > userspace on these in the long run to handle head unloading, but we need > to figure out the best interface for that first. Code based on a patch by > Nicolas Boichat and the FreeBSD kernel code. > > Signed-off-by: Matthew Garrett <mjg@xxxxxxxxxx> > Cc: Nicolas Boichat <nicolas@xxxxxxxxxx> Not sure if the overall code makes sense w/o actual use of the interrupts, just to fill up the log. I'd like to get some feedback from others on this. > --- > drivers/hwmon/applesmc.c | 138 +++++++++++++++++++++++++++++++++++++++++----- > 1 files changed, 123 insertions(+), 15 deletions(-) > > diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c > index 4540bb1..ac2f9a2 100644 > --- a/drivers/hwmon/applesmc.c > +++ b/drivers/hwmon/applesmc.c > @@ -45,11 +45,13 @@ > #include <linux/workqueue.h> > #include <linux/slab.h> > #include <linux/pnp.h> > +#include <linux/interrupt.h> > > /* data port used by Apple SMC */ > #define APPLESMC_DATA_PORT 0 > /* command/status port used by Apple SMC */ > #define APPLESMC_CMD_PORT 0x4 > +#define APPLESMC_INT_PORT 0x1f > > #define APPLESMC_NR_PORTS 32 > > @@ -78,6 +80,14 @@ > #define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */ > #define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */ > > +#define NOTIFY_KEY "NTOK" /* r/w ui8 */ > + > +#define NOTIFY_LOW_RATE "MOLD" /* r/w ui8 */ > +#define NOTIFY_HIGH_RATE "MOHD" /* r/w ui8 */ > +#define NOTIFY_LOW_THRESH "MOLT" /* r/w ui16 */ > +#define NOTIFY_HIGH_THRESH "MOHT" /* r/w ui16 */ > +#define NOTIFY_FLAG "MSDW" /* r/w ui8 */ > + > #define FANS_COUNT "FNum" /* r-o ui8 */ > #define FANS_MANUAL "FS! " /* r-w ui16 */ > #define FAN_ID_FMT "F%dID" /* r-o char[16] */ > @@ -147,6 +157,7 @@ struct applesmc_registers { > struct applesmc_dev { > int iobase; > int iolen; > + int irq; > struct work_struct backlight_work; > struct applesmc_registers smcreg; > /* > @@ -447,6 +458,37 @@ static int applesmc_write_key(struct applesmc_dev *pdev, const char *key, > return applesmc_write_entry(pdev, entry, buffer, len); > } > > +static int applesmc_write_key_check(struct applesmc_dev *pdev, const char *key, > + u8 *buffer, u8 len) > +{ > + int total, ret, i, compare; > + > + u8 rdbuffer[APPLESMC_MAX_DATA_LENGTH]; > + > + for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { > + ret = applesmc_read_key(pdev, key, rdbuffer, len); > + if (!ret) { > + compare = 1; > + for (i = 0; i < len; i++) { > + if (rdbuffer[i] != buffer[i]) { > + compare = 0; > + break; > + } > + } > + > + if (compare) > + return 0; Why not use memcmp() ? > + } > + ret = applesmc_write_key(pdev, key, buffer, len); > + msleep(INIT_WAIT_MSECS); > + } > + > + if (ret) > + return ret; > + else > + return -EIO; else is unnecessary. > +} > + > static int applesmc_has_key(struct applesmc_dev *pdev, const char *key, > bool *value) > { > @@ -489,27 +531,49 @@ static int applesmc_read_motion_sensor(struct applesmc_dev *pdev, int index, > } > > /* > - * applesmc_device_init - initialize the accelerometer. Can sleep. > + * applesmc_accel_init - initialize the accelerometer. Can sleep. > */ > -static void applesmc_device_init(struct applesmc_dev *pdev) > +static void applesmc_accel_init(struct applesmc_dev *pdev) > { > - int total; > + int ret; > u8 buffer[2]; > > if (!pdev->smcreg.has_accelerometer) > return; > > - for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { > - if (!applesmc_read_key(pdev, MOTION_SENSOR_KEY, buffer, 2) && > - (buffer[0] != 0x00 || buffer[1] != 0x00)) > - return; > - buffer[0] = 0xe0; > - buffer[1] = 0x00; > - applesmc_write_key(pdev, MOTION_SENSOR_KEY, buffer, 2); > - msleep(INIT_WAIT_MSECS); > - } > + buffer[0] = 20; > + ret = applesmc_write_key_check(pdev, NOTIFY_LOW_RATE, buffer, 1); > + if (ret) > + goto out; > + > + ret = applesmc_write_key_check(pdev, NOTIFY_HIGH_RATE, buffer, 1); > + if (ret) > + goto out; > > - pr_warn("failed to init the device\n"); > + buffer[0] = 0; > + buffer[1] = 0x60; > + ret = applesmc_write_key_check(pdev, NOTIFY_LOW_THRESH, buffer, 2); > + if (ret) > + goto out; > + > + buffer[0] = 1; > + buffer[1] = 0xc0; > + ret = applesmc_write_key_check(pdev, NOTIFY_HIGH_THRESH, buffer, 2); > + if (ret) > + goto out; > + > + buffer[0] = 0xe0; > + buffer[1] = 0xf8; > + ret = applesmc_write_key_check(pdev, MOTION_SENSOR_KEY, buffer, 2); > + > + buffer[0] = 1; > + ret = applesmc_write_key(pdev, NOTIFY_FLAG, buffer, 1); > + if (ret) > + goto out; > + > +out: > + if (ret) > + pr_warn("failed to init the accelerometer\n"); > } > > /* > @@ -629,7 +693,7 @@ static int applesmc_pm_restore(struct device *dev) > struct pnp_dev *pnpdev = to_pnp_dev(dev); > struct applesmc_dev *pdev = pnp_get_drvdata(pnpdev); > > - applesmc_device_init(pdev); > + applesmc_accel_init(pdev); > return applesmc_pm_resume(dev); > } > > @@ -1265,10 +1329,36 @@ static void applesmc_release_key_backlight(struct applesmc_dev *pdev) > destroy_workqueue(pdev->applesmc_led_wq); > } > > +static irqreturn_t applesmc_interrupt(int irq, void *d) > +{ > + struct applesmc_dev *pdev = d; > + u8 intr = applesmc_read_reg(pdev, APPLESMC_INT_PORT); > + > + switch (intr) { > + case 0x60: > + printk(KERN_INFO "applesmc: Free fall interrupt\n"); Please use pr_info(). Also, if we in fact decide to accept this code, it might make sense to limit the amount of messages. > + break; > + case 0x6f: > + printk(KERN_INFO "applesmc: High acceleration interrupt\n"); > + break; > + case 0x80: > + printk(KERN_INFO "applesmc: Shock interrupt\n"); > + break; > + default: > + if (intr) > + printk(KERN_INFO "applesmc: Unknown interrupt %x\n", > + intr); > + break; > + } > + > + return IRQ_HANDLED; > +} > + > static int __devinit applesmc_pnp_probe(struct pnp_dev *dev, > const struct pnp_device_id *dev_id) > { > int ret; > + u8 buffer = 1; > struct resource *res; > struct applesmc_dev *pdev = kzalloc(sizeof(struct applesmc_dev), > GFP_KERNEL); > @@ -1295,6 +1385,15 @@ static int __devinit applesmc_pnp_probe(struct pnp_dev *dev, > goto out; > } > > + res = pnp_get_resource(dev, IORESOURCE_IRQ, 0); > + > + if (res) { > + ret = request_irq(res->start, applesmc_interrupt, > + IRQF_DISABLED, "applesmc", pdev); > + if (!ret) > + pdev->irq = res->start; > + } > + > /* create register cache */ > > ret = applesmc_init_smcreg(pdev); > @@ -1333,7 +1432,9 @@ static int __devinit applesmc_pnp_probe(struct pnp_dev *dev, > > INIT_WORK(&pdev->backlight_work, &applesmc_backlight_set); > > - applesmc_device_init(pdev); > + applesmc_accel_init(pdev); > + > + applesmc_write_key(pdev, NOTIFY_KEY, &buffer, 1); > > return 0; > > @@ -1352,6 +1453,8 @@ out_info: > out_smcreg: > applesmc_destroy_smcreg(pdev); > out_region: > + if (pdev->irq) > + free_irq(pdev->irq, pdev); > release_region(pdev->iobase, pdev->iolen); > out: > kfree(pdev); > @@ -1362,7 +1465,12 @@ out: > static void __devexit applesmc_pnp_remove(struct pnp_dev *dev) > { > struct applesmc_dev *pdev = pnp_get_drvdata(dev); > + u8 buffer = 0; > > + if (pdev->irq) { > + applesmc_write_key(pdev, NOTIFY_KEY, &buffer, 1); > + free_irq(pdev->irq, pdev); > + } > hwmon_device_unregister(pdev->hwmon_dev); > applesmc_release_key_backlight(pdev); > applesmc_release_light_sensor(pdev); _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors