[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <48479922.6050003@movial.fi>
Date: Thu, 05 Jun 2008 10:43:30 +0300
From: Riku Voipio <riku.voipio@...ial.fi>
To: Yan Burman <burman.yan@...il.com>
CC: LKML <linux-kernel@...r.kernel.org>,
Eric Piel <eric.piel@...mplin-utc.net>,
HWMON <lm-sensors@...sensors.org>,
spi-devel-general@...ts.sourceforge.net, jic23@....ac.uk
Subject: Re: [lm-sensors] [PATCH 2.6.25.4] hwmon: HP Mobile Data Protection
System 3D ACPI driver
Yan Burman wrote:
> +==================
> +
> +Supported chips:
> +
> + * STMicroelectronics LIS3LV02DL and LIS3LV02DQ
> +
These chips are connected to either I2C or SPI - This is the 4th driver for
(apparently) these same chips:
http://docwiki.gumstix.org/Lis3lv02dq_spi.c
http://svn.openmoko.org/branches/src/target/kernel/2.6.24.x/patches/lis302dl.patch
http://article.gmane.org/gmane.linux.kernel.spi.devel/1010
> + depends on ACPI && INPUT && X86
>
> +/* The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ
> + * that seems to be connected via SPI */
>
Perhaps it would make more sense implement support for SPI
bus on the laptop and use the SPI interface directly instead or
routing via the ACPI hiding layer?
> +
> +#define MDPS_WHO_AM_I 0x0F /*r 00111010 */
> +#define MDPS_OFFSET_X 0x16 /*rw */
> +#define MDPS_OFFSET_Y 0x17 /*rw */
> +#define MDPS_OFFSET_Z 0x18 /*rw */
> +#define MDPS_GAIN_X 0x19 /*rw */
> +#define MDPS_GAIN_Y 0x1A /*rw */
> +#define MDPS_GAIN_Z 0x1B /*rw */
> +#define MDPS_CTRL_REG1 0x20 /*rw 00000111 */
> +#define MDPS_CTRL_REG2 0x21 /*rw 00000000 */
> +#define MDPS_CTRL_REG3 0x22 /*rw 00001000 */
> +#define MDPS_HP_FILTER RESET 0x23 /*r */
> +#define MDPS_STATUS_REG 0x27 /*rw 00000000 */
> +#define MDPS_OUTX_L 0x28 /*r */
> +#define MDPS_OUTX_H 0x29 /*r */
> +#define MDPS_OUTY_L 0x2A /*r */
> +#define MDPS_OUTY_H 0x2B /*r */
> +#define MDPS_OUTZ_L 0x2C /*r */
> +#define MDPS_OUTZ_H 0x2D /*r */
> +#define MDPS_FF_WU_CFG 0x30 /*rw 00000000 */
> +#define MDPS_FF_WU_SRC 0x31 /*rw 00000000 */
> +#define MDPS_FF_WU_ACK 0x32 /*r */
> +#define MDPS_FF_WU_THS_L 0x34 /*rw 00000000 */
> +#define MDPS_FF_WU_THS_H 0x35 /*rw 00000000 */
> +#define MDPS_FF_WU_DURATION 0x36 /*rw 00000000 */
> +#define MDPS_DD_CFG 0x38 /*rw 00000000 */
> +#define MDPS_DD_SRC 0x39 /*rw 00000000 */
> +#define MDPS_DD_ACK 0x3A /*r */
> +#define MDPS_DD_THSI_L 0x3C /*rw 00000000 */
> +#define MDPS_DD_THSI_H 0x3D /*rw 00000000 */
> +#define MDPS_DD_THSE_L 0x3E /*rw 00000000 */
> +#define MDPS_DD_THSE_H 0x3F /*rw 00000000 */
> +
> +#define MDPS_ID 0x3A /* MDPS_WHO_AM_I */
> +#define MDPS_FS (1<<7) /* MDPS_CTRL_REG2 : Full Scale selection */
> +#define MDPS_BDU (1<<6) /* MDPS_CTRL_REG2 : Block Data Update */
> +
> +/* joystick device poll interval in milliseconds */
> +#define MDPS_POLL_INTERVAL 30
> +
> +/* Maximum value our axis may get for the input device */
> +#define MDPS_MAX_VAL 2048
> +
> +static unsigned int power_off;
> +module_param(power_off, bool, S_IRUGO);
> +MODULE_PARM_DESC(power_off, "Turn off device on module load");
> +
> +struct axis_conversion {
> + s8 x;
> + s8 y;
> + s8 z;
> +};
> +
> +struct acpi_mdps {
> + struct acpi_device *device; /* The ACPI device */
> + u32 irq; /* IRQ number */
> + struct input_dev *idev; /* input device */
> + struct task_struct *kthread; /* kthread for input */
> + int xcalib; /* calibrated null value for x */
> + int ycalib; /* calibrated null value for y */
> + int zcalib; /* calibrated null value for z */
> + int is_on; /* whether the device is on or off */
> + struct platform_device *pdev; /* platform device */
> + atomic_t count; /* interrupt count after last read */
> + struct fasync_struct *async_queue;
> + atomic_t available; /* whether the device is open */
> + wait_queue_head_t misc_wait; /* Wait queue for the misc device */
> + /* conversion between hw axis and logical ones */
> + struct axis_conversion ac;
> +};
> +
> +static struct acpi_mdps mdps;
> +
> +static int mdps_remove_fs(void);
> +static int mdps_add_fs(struct acpi_device *device);
> +static void mdps_joystick_enable(void);
> +static void mdps_joystick_disable(void);
> +
> +static struct acpi_device_id mdps_device_ids[] = {
> + {ACPI_MDPS_ID, 0},
> + {"", 0},
> +};
> +MODULE_DEVICE_TABLE(acpi, mdps_device_ids);
> +
> +/** Create a single value from 2 bytes received from the accelerometer
> + * @param hi the high byte
> + * @param lo the low byte
> + * @return the resulting value
> + */
> +static inline s16 mdps_glue_bytes(unsigned long hi, unsigned long lo)
> +{
> + /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */
> + return (s16)((hi << 8) | lo);
> +}
> +
> +/** ACPI ALRD method: read a register
> + * @param handle the handle of the device
> + * @param reg the register to read
> + * @param[out] ret result of the operation
> + * @return AE_OK on success
> + */
> +static acpi_status mdps_ALRD(acpi_handle handle, int reg,
> + unsigned long *ret)
> +{
> + union acpi_object arg0 = { ACPI_TYPE_INTEGER };
> + struct acpi_object_list args = { 1, &arg0 };
> +
> + arg0.integer.value = reg;
> +
> + return acpi_evaluate_integer(handle, "ALRD", &args, ret);
> +}
> +
> +/** ACPI _INI method: initialize the device.
> + * @param handle the handle of the device
> + * @return 0 on success
> + */
> +static inline acpi_status mdps__INI(acpi_handle handle)
> +{
> + return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL);
> +}
> +
> +/** ACPI ALWR method: write to a register
> + * @param handle the handle of the device
> + * @param reg the register to write to
> + * @param val the value to write
> + * @param[out] ret result of the operation
> + * @return AE_OK on success
> + */
> +static acpi_status mdps_ALWR(acpi_handle handle, int reg, int val,
> + unsigned long *ret)
> +{
> + union acpi_object in_obj[2];
> + struct acpi_object_list args = { 2, in_obj };
> +
> + in_obj[0].type = ACPI_TYPE_INTEGER;
> + in_obj[0].integer.value = reg;
> + in_obj[1].type = ACPI_TYPE_INTEGER;
> + in_obj[1].integer.value = val;
> +
> + return acpi_evaluate_integer(handle, "ALWR", &args, ret);
> +}
> +
> +static int mdps_read_axis(acpi_handle handle, int lo_const, int hi_const)
> +{
> + unsigned long lo_val, hi_val;
> + mdps_ALRD(handle, lo_const, &lo_val);
> + mdps_ALRD(handle, hi_const, &hi_val);
> + return mdps_glue_bytes(hi_val, lo_val);
> +}
> +
> +static inline int mdps_get_axis(s8 axis, int hw_values[3])
> +{
> + if (axis > 0)
> + return hw_values[axis - 1];
> + else
> + return -hw_values[-axis - 1];
> +}
> +
> +/** Get X, Y and Z axis values from the accelerometer
> + * @param handle the handle to the device
> + * @param[out] x where to store the X axis value
> + * @param[out] y where to store the Y axis value
> + * @param[out] z where to store the Z axis value
> + * @note 40Hz input device can eat up about 10% CPU at 800MHZ
> + */
> +static void mdps_get_xyz(acpi_handle handle, int *x, int *y, int *z)
> +{
> + int position[3];
> + position[0] = mdps_read_axis(handle, MDPS_OUTX_L, MDPS_OUTX_H);
> + position[1] = mdps_read_axis(handle, MDPS_OUTY_L, MDPS_OUTY_H);
> + position[2] = mdps_read_axis(handle, MDPS_OUTZ_L, MDPS_OUTZ_H);
> +
> + *x = mdps_get_axis(mdps.ac.x, position);
> + *y = mdps_get_axis(mdps.ac.y, position);
> + *z = mdps_get_axis(mdps.ac.z, position);
> +}
> +
> +/** Kthread polling function
> + * @param data unused - here to conform to threadfn prototype
> + */
> +static int mdps_input_kthread(void *data)
> +{
> + int x, y, z;
> +
> + while (!kthread_should_stop()) {
> + mdps_get_xyz(mdps.device->handle, &x, &y, &z);
> + input_report_abs(mdps.idev, ABS_X, x - mdps.xcalib);
> + input_report_abs(mdps.idev, ABS_Y, y - mdps.ycalib);
> + input_report_abs(mdps.idev, ABS_Z, z - mdps.zcalib);
> +
> + input_sync(mdps.idev);
> +
> + try_to_freeze();
> + msleep_interruptible(MDPS_POLL_INTERVAL);
> + }
> +
> + return 0;
> +}
> +
> +static inline void mdps_poweroff(acpi_handle handle)
> +{
> + unsigned long ret;
> + mdps.is_on = 0;
> + /* disable X,Y,Z axis and power down */
> + mdps_ALWR(handle, MDPS_CTRL_REG1, 0x00, &ret);
> +}
> +
> +static inline void mdps_poweron(acpi_handle handle)
> +{
> + unsigned long val, retw;
> +
> + mdps.is_on = 1;
> + mdps__INI(handle);
> + /*
> + * Change to Block Data Update mode: LSB and MSB values are not updated
> + * until both have been read. So the value read will always be correct.
> + */
> + mdps_ALRD(handle, MDPS_CTRL_REG2, &val);
> + val |= MDPS_BDU;
> + mdps_ALWR(handle, MDPS_CTRL_REG2, val, &retw);
> +}
> +
> +#ifdef CONFIG_PM
> +static int mdps_suspend(struct acpi_device *device, pm_message_t state)
> +{
> + /* make sure the device is off when we suspend */
> + mdps_poweroff(mdps.device->handle);
> + return 0;
> +}
> +#endif
> +
> +static int mdps_resume(struct acpi_device *device)
> +{
> + /* make sure the device went online */
> + mdps_poweron(mdps.device->handle);
> + return 0;
> +}
> +
> +static irqreturn_t mdps_irq(int irq, void *dev_id)
> +{
> + atomic_inc(&mdps.count);
> +
> + wake_up_interruptible(&mdps.misc_wait);
> + kill_fasync(&mdps.async_queue, SIGIO, POLL_IN);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int mdps_misc_open(struct inode *inode, struct file *file)
> +{
> + int ret;
> +
> + if (!atomic_dec_and_test(&mdps.available)) {
> + atomic_inc(&mdps.available);
> + return -EBUSY; /* already open */
> + }
> +
> + atomic_set(&mdps.count, 0);
> +
> + /* Can't have shared interrupts here, since we have no way
> + * to determine in interrupt context
> + * if it was our device that caused the interrupt */
> + ret = request_irq(mdps.irq, mdps_irq, 0, "mdps", mdps_irq);
> + if (ret) {
> + atomic_inc(&mdps.available);
> + printk(KERN_ERR "mdps: IRQ%d allocation failed\n", mdps.irq);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int mdps_misc_release(struct inode *inode, struct file *file)
> +{
> + fasync_helper(-1, file, 0, &mdps.async_queue);
> + free_irq(mdps.irq, mdps_irq);
> + atomic_inc(&mdps.available); /* release the device */
> + return 0;
> +}
> +
> +static ssize_t mdps_misc_read(struct file *file, char __user *buf,
> + size_t count, loff_t *pos)
> +{
> + DECLARE_WAITQUEUE(wait, current);
> + u32 data;
> + ssize_t retval = count;
> +
> + if (count != sizeof(u32))
> + return -EINVAL;
> +
> + add_wait_queue(&mdps.misc_wait, &wait);
> + for (; ; ) {
> + set_current_state(TASK_INTERRUPTIBLE);
> + data = atomic_xchg(&mdps.count, 0);
> + if (data)
> + break;
> +
> + if (file->f_flags & O_NONBLOCK) {
> + retval = -EAGAIN;
> + goto out;
> + }
> +
> + if (signal_pending(current)) {
> + retval = -ERESTARTSYS;
> + goto out;
> + }
> +
> + schedule();
> + }
> +
> + /* make sure we are not going into copy_to_user() with
> + * TASK_INTERRUPTIBLE state */
> + set_current_state(TASK_RUNNING);
> + if (copy_to_user(buf, &data, sizeof(data)))
> + retval = -EFAULT;
> +
> +out:
> + __set_current_state(TASK_RUNNING);
> + remove_wait_queue(&mdps.misc_wait, &wait);
> +
> + return retval;
> +}
> +
> +static unsigned int mdps_misc_poll(struct file *file, poll_table *wait)
> +{
> + poll_wait(file, &mdps.misc_wait, wait);
> + if (atomic_read(&mdps.count))
> + return POLLIN | POLLRDNORM;
> + return 0;
> +}
> +
> +static int mdps_misc_fasync(int fd, struct file *file, int on)
> +{
> + return fasync_helper(fd, file, on, &mdps.async_queue);
> +}
> +
> +static const struct file_operations mdps_misc_fops = {
> + .owner = THIS_MODULE,
> + .llseek = no_llseek,
> + .read = mdps_misc_read,
> + .open = mdps_misc_open,
> + .release = mdps_misc_release,
> + .poll = mdps_misc_poll,
> + .fasync = mdps_misc_fasync,
> +};
> +
> +static struct miscdevice mdps_misc_device = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = "accel",
> + .fops = &mdps_misc_fops,
> +};
> +
> +static acpi_status
> +mdps_get_resource(struct acpi_resource *resource, void *context)
> +{
> + if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
> + struct acpi_resource_extended_irq *irq;
> + u32 *device_irq = context;
> +
> + irq = &resource->data.extended_irq;
> + *device_irq = irq->interrupts[0];
> + }
> +
> + return AE_OK;
> +}
> +
> +static void mdps_enum_resources(struct acpi_device *device)
> +{
> + acpi_status status;
> +
> + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
> + mdps_get_resource, &mdps.irq);
> + if (ACPI_FAILURE(status))
> + printk(KERN_DEBUG "mdps: Error getting resources\n");
> +}
> +
> +static int mdps_dmi_matched(const struct dmi_system_id *dmi)
> +{
> + mdps.ac = *(struct axis_conversion *)dmi->driver_data;
> + printk(KERN_INFO "mdps: hardware type %s found.\n", dmi->ident);
> +
> + return 1;
> +}
> +
> +
> +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1).
> + * If the value is negative, the opposite of the hw value is used. */
> +static struct axis_conversion mdps_axis_normal = {1, 2, 3};
> +static struct axis_conversion mdps_axis_y_inverted = {1, -2, 3};
> +static struct axis_conversion mdps_axis_x_inverted = {-1, 2, 3};
> +
> +static struct dmi_system_id mdps_dmi_ids[] = {
> + {
> + .callback = mdps_dmi_matched,
> + .ident = "NC64x0",
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nc64"),
> + },
> + .driver_data = &mdps_axis_x_inverted
> + },
> + {
> + .callback = mdps_dmi_matched,
> + .ident = "NX9420",
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx9420"),
> + },
> + .driver_data = &mdps_axis_x_inverted
> + },
> + {
> + .callback = mdps_dmi_matched,
> + .ident = "NW9440",
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nw9440"),
> + },
> + .driver_data = &mdps_axis_x_inverted
> + },
> + {
> + .callback = mdps_dmi_matched,
> + .ident = "NC2510",
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 2510"),
> + },
> + .driver_data = &mdps_axis_y_inverted
> + },
> + { NULL, }
> +/* Laptop models without axis info (yet):
> + * "NC84x0" "HP Compaq nc84"
> + * "NC651xx" "HP Compaq 651"
> + * "NC671xx" "HP Compaq 671"
> + * "NC6910" "HP Compaq 6910"
> + * HP Compaq 8510x Notebook PC / Mobile Workstation
> + * HP Compaq 8710x Notebook PC / Mobile Workstation
> + * "NC2400" "HP Compaq nc2400"
> + * "NX74x0" "HP Compaq nx74"
> + * "NX6325" "HP Compaq nx6325"
> + * "NC4400" "HP Compaq nc4400"
> + */
> +};
> +
> +static int mdps_add(struct acpi_device *device)
> +{
> + unsigned long val;
> + int ret;
> +
> + if (!device)
> + return -EINVAL;
> +
> + mdps.device = device;
> + strcpy(acpi_device_name(device), DRIVER_NAME);
> + strcpy(acpi_device_class(device), ACPI_MDPS_CLASS);
> + acpi_driver_data(device) = &mdps;
> +
> + mdps_ALRD(device->handle, MDPS_WHO_AM_I, &val);
> + if (val != MDPS_ID) {
> + printk(KERN_ERR
> + "mdps: Accelerometer chip not LIS3LV02D{L,Q}\n");
> + return -ENODEV;
> + }
> +
> + /* This is just to make sure that the same physical move
> + * is reported identically */
> + if (dmi_check_system(mdps_dmi_ids) == 0) {
> + printk(KERN_INFO "mdps: laptop model unknown, "
> + "using default axes configuration\n");
> + mdps.ac = mdps_axis_normal;
> + }
> +
> + mdps_add_fs(device);
> + mdps_resume(device);
> +
> + mdps_joystick_enable();
> +
> + /* obtain IRQ number of our device from ACPI */
> + mdps_enum_resources(device);
> +
> + if (power_off) /* see if user wanted to power off the device on load */
> + mdps_poweroff(mdps.device->handle);
> +
> + /* if we did not get an IRQ from ACPI - we have nothing more to do */
> + if (!mdps.irq) {
> + printk(KERN_INFO
> + "mdps: No IRQ in ACPI. Disabling /dev/accel\n");
> + return 0;
> + }
> +
> + atomic_set(&mdps.available, 1); /* init the misc device open count */
> + init_waitqueue_head(&mdps.misc_wait);
> +
> + ret = misc_register(&mdps_misc_device);
> + if (ret)
> + printk(KERN_ERR "mdps: misc_register failed\n");
> +
> + return 0;
> +}
> +
> +static int mdps_remove(struct acpi_device *device, int type)
> +{
> + if (!device)
> + return -EINVAL;
> +
> + if (mdps.irq)
> + misc_deregister(&mdps_misc_device);
> +
> + mdps_joystick_disable();
> +
> + return mdps_remove_fs();
> +}
> +
> +static inline void mdps_calibrate_joystick(void)
> +{
> + mdps_get_xyz(mdps.device->handle, &mdps.xcalib, &mdps.ycalib,
> + &mdps.zcalib);
> +}
> +
> +static int mdps_joystick_open(struct input_dev *dev)
> +{
> + mdps.kthread = kthread_run(mdps_input_kthread, NULL, "kmdps");
> + if (IS_ERR(mdps.kthread))
> + return PTR_ERR(mdps.kthread);
> +
> + return 0;
> +}
> +
> +static void mdps_joystick_close(struct input_dev *dev)
> +{
> + kthread_stop(mdps.kthread);
> +}
> +
> +static void mdps_joystick_enable(void)
> +{
> + if (mdps.idev)
> + return;
> +
> + mdps.idev = input_allocate_device();
> + if (!mdps.idev)
> + return;
> +
> + mdps_calibrate_joystick();
> +
> + mdps.idev->name = "HP Mobile Data Protection System";
> + mdps.idev->phys = "mdps/input0";
> + mdps.idev->id.bustype = BUS_HOST;
> + mdps.idev->id.vendor = 0;
> + mdps.idev->dev.parent = &mdps.pdev->dev;
> +
> + set_bit(EV_ABS, mdps.idev->evbit);
> +
> + input_set_abs_params(mdps.idev, ABS_X, -MDPS_MAX_VAL, MDPS_MAX_VAL,
> + 3, 0);
> + input_set_abs_params(mdps.idev, ABS_Y, -MDPS_MAX_VAL, MDPS_MAX_VAL,
> + 3, 0);
> + input_set_abs_params(mdps.idev, ABS_Z, -MDPS_MAX_VAL, MDPS_MAX_VAL,
> + 3, 0);
> +
> + mdps.idev->open = mdps_joystick_open;
> + mdps.idev->close = mdps_joystick_close;
> +
> + if (input_register_device(mdps.idev)) {
> + input_free_device(mdps.idev);
> + mdps.idev = NULL;
> + }
> +}
> +
> +static void mdps_joystick_disable(void)
> +{
> + if (!mdps.idev)
> + return;
> +
> + input_unregister_device(mdps.idev);
> + mdps.idev = NULL;
> +}
> +
> +/* Sysfs stuff */
> +static ssize_t mdps_position_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int x, y, z;
> + mdps_get_xyz(mdps.device->handle, &x, &y, &z);
> +
> + return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
> +}
> +
> +static ssize_t mdps_state_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sprintf(buf, "%s\n", (mdps.is_on ? "on" : "off"));
> +}
> +
> +static ssize_t mdps_calibrate_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sprintf(buf, "(%d,%d,%d)\n", mdps.xcalib, mdps.ycalib,
> + mdps.zcalib);
> +}
> +
> +static ssize_t mdps_calibrate_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + mdps_calibrate_joystick();
> + return count;
> +}
> +
> +static ssize_t mdps_rate_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + unsigned long ctrl;
> + int rate = 0;
> +
> + mdps_ALRD(mdps.device->handle, MDPS_CTRL_REG1, &ctrl);
> +
> + /* get the sampling rate of the accelerometer in HZ */
> + switch ((ctrl & 0x30) >> 4) {
> + case 00:
> + rate = 40;
> + break;
> +
> + case 01:
> + rate = 160;
> + break;
> +
> + case 02:
> + rate = 640;
> + break;
> +
> + case 03:
> + rate = 2560;
> + break;
> + }
> +
> + return sprintf(buf, "%d\n", rate);
> +}
> +
> +static ssize_t mdps_state_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int state;
> + if (sscanf(buf, "%d", &state) != 1 || (state != 1 && state != 0))
> + return -EINVAL;
> +
> + mdps.is_on = state;
> +
> + if (mdps.is_on)
> + mdps_poweron(mdps.device->handle);
> + else
> + mdps_poweroff(mdps.device->handle);
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(position, S_IRUGO, mdps_position_show, NULL);
> +static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, mdps_calibrate_show,
> + mdps_calibrate_store);
> +static DEVICE_ATTR(rate, S_IRUGO, mdps_rate_show, NULL);
> +static DEVICE_ATTR(state, S_IRUGO|S_IWUSR, mdps_state_show, mdps_state_store);
> +
> +static struct attribute *mdps_attributes[] = {
> + &dev_attr_position.attr,
> + &dev_attr_calibrate.attr,
> + &dev_attr_rate.attr,
> + &dev_attr_state.attr,
> + NULL
> +};
> +
> +static struct attribute_group mdps_attribute_group = {
> + .attrs = mdps_attributes
> +};
> +
> +static int mdps_add_fs(struct acpi_device *device)
> +{
> + mdps.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
> + if (IS_ERR(mdps.pdev))
> + return PTR_ERR(mdps.pdev);
> +
> + return sysfs_create_group(&mdps.pdev->dev.kobj, &mdps_attribute_group);
> +}
> +
> +static int mdps_remove_fs(void)
> +{
> + sysfs_remove_group(&mdps.pdev->dev.kobj, &mdps_attribute_group);
> + platform_device_unregister(mdps.pdev);
> + return 0;
> +}
> +
> +static struct acpi_driver mdps_driver = {
> + .name = DRIVER_NAME,
> + .class = ACPI_MDPS_CLASS,
> + .ids = mdps_device_ids,
> + .ops = {
> + .add = mdps_add,
> + .remove = mdps_remove,
> +#ifdef CONFIG_PM
> + .suspend = mdps_suspend,
> + .resume = mdps_resume
> +#endif
> + }
> +};
> +
> +static int __init mdps_init_module(void)
> +{
> + int ret;
> +
> + if (acpi_disabled)
> + return -ENODEV;
> +
> + ret = acpi_bus_register_driver(&mdps_driver);
> + if (ret < 0)
> + return ret;
> +
> + printk(KERN_INFO "mdps version " VERSION " loaded.\n");
> +
> + return 0;
> +}
> +
> +static void __exit mdps_exit_module(void)
> +{
> + acpi_bus_unregister_driver(&mdps_driver);
> +}
> +
> +MODULE_DESCRIPTION("HP three-axis digital accelerometer ACPI driver");
> +MODULE_AUTHOR("Yan Burman (burman.yan@...il.com)");
> +MODULE_VERSION(VERSION);
> +MODULE_LICENSE("GPL");
> +
> +module_init(mdps_init_module);
> +module_exit(mdps_exit_module);
>
>
> _______________________________________________
> lm-sensors mailing list
> lm-sensors@...sensors.org
> http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists