From 335d7c58fcc1b71387a2c38b610b08bb9d3a6fcc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Jan 2011 11:45:49 -0800 Subject: i2c/busses: Add support for Diolan U2C-12 USB-I2C adapter This patch adds support for the Diolan U2C-12 USB-I2C adapter. It also updates MAINTAINERS to list the author as maintainer. Signed-off-by: Guenter Roeck Signed-off-by: Ben Dooks --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f3a5c09cc..74cad4fa5ae5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2093,6 +2093,12 @@ F: Documentation/serial/digiepca.txt F: drivers/char/epca* F: drivers/char/digi* +DIOLAN U2C-12 I2C DRIVER +M: Guenter Roeck +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/busses/i2c-diolan-u2c.c + DIRECTORY NOTIFICATION (DNOTIFY) M: Eric Paris S: Maintained -- cgit v1.2.3 From 830e6384e100dc758392c41f68c807c002945928 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 23 Feb 2011 16:34:17 +0200 Subject: MAINTAINERS: Update OMAP DSS maintainer Signed-off-by: Tomi Valkeinen --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 939e852ef74d..ee484254dcb5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4514,14 +4514,14 @@ S: Maintained F: sound/soc/omap/ OMAP FRAMEBUFFER SUPPORT -M: Tomi Valkeinen +M: Tomi Valkeinen L: linux-fbdev@vger.kernel.org L: linux-omap@vger.kernel.org S: Maintained F: drivers/video/omap/ OMAP DISPLAY SUBSYSTEM and FRAMEBUFFER SUPPORT (DSS2) -M: Tomi Valkeinen +M: Tomi Valkeinen L: linux-omap@vger.kernel.org L: linux-fbdev@vger.kernel.org S: Maintained -- cgit v1.2.3 From ff606677f6a47c63329cf8e6c7cf978c29f2d736 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 21 Mar 2011 17:59:36 +0100 Subject: Move lis3lv02d drivers to drivers/misc The lis3lv02d drivers aren't hardware monitoring drivers, so the don't belong to drivers/hwmon. Move them to drivers/misc, short of a better home. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck Acked-by: Eric Piel Acked-by: Jonathan Cameron Tested-by: Eric Piel Tested-by: Takashi Iwai --- Documentation/hwmon/lis3lv02d | 92 --- Documentation/misc-devices/lis3lv02d | 92 +++ MAINTAINERS | 4 +- drivers/hwmon/Kconfig | 40 -- drivers/hwmon/Makefile | 5 - drivers/hwmon/lis3lv02d.c | 1000 -------------------------------- drivers/hwmon/lis3lv02d.h | 291 ---------- drivers/hwmon/lis3lv02d_i2c.c | 279 --------- drivers/hwmon/lis3lv02d_spi.c | 145 ----- drivers/misc/Kconfig | 9 + drivers/misc/Makefile | 1 + drivers/misc/lis3lv02d/Kconfig | 37 ++ drivers/misc/lis3lv02d/Makefile | 7 + drivers/misc/lis3lv02d/lis3lv02d.c | 999 +++++++++++++++++++++++++++++++ drivers/misc/lis3lv02d/lis3lv02d.h | 291 ++++++++++ drivers/misc/lis3lv02d/lis3lv02d_i2c.c | 279 +++++++++ drivers/misc/lis3lv02d/lis3lv02d_spi.c | 145 +++++ drivers/platform/x86/hp_accel.c | 2 +- 18 files changed, 1863 insertions(+), 1855 deletions(-) delete mode 100644 Documentation/hwmon/lis3lv02d create mode 100644 Documentation/misc-devices/lis3lv02d delete mode 100644 drivers/hwmon/lis3lv02d.c delete mode 100644 drivers/hwmon/lis3lv02d.h delete mode 100644 drivers/hwmon/lis3lv02d_i2c.c delete mode 100644 drivers/hwmon/lis3lv02d_spi.c create mode 100644 drivers/misc/lis3lv02d/Kconfig create mode 100644 drivers/misc/lis3lv02d/Makefile create mode 100644 drivers/misc/lis3lv02d/lis3lv02d.c create mode 100644 drivers/misc/lis3lv02d/lis3lv02d.h create mode 100644 drivers/misc/lis3lv02d/lis3lv02d_i2c.c create mode 100644 drivers/misc/lis3lv02d/lis3lv02d_spi.c (limited to 'MAINTAINERS') diff --git a/Documentation/hwmon/lis3lv02d b/Documentation/hwmon/lis3lv02d deleted file mode 100644 index f1a4ec840f86..000000000000 --- a/Documentation/hwmon/lis3lv02d +++ /dev/null @@ -1,92 +0,0 @@ -Kernel driver lis3lv02d -======================= - -Supported chips: - - * STMicroelectronics LIS3LV02DL, LIS3LV02DQ (12 bits precision) - * STMicroelectronics LIS302DL, LIS3L02DQ, LIS331DL (8 bits) - -Authors: - Yan Burman - Eric Piel - - -Description ------------ - -This driver provides support for the accelerometer found in various HP laptops -sporting the feature officially called "HP Mobile Data Protection System 3D" or -"HP 3D DriveGuard". It detects automatically laptops with this sensor. Known -models (full list can be found in drivers/platform/x86/hp_accel.c) will have -their axis automatically oriented on standard way (eg: you can directly play -neverball). The accelerometer data is readable via -/sys/devices/platform/lis3lv02d. Reported values are scaled -to mg values (1/1000th of earth gravity). - -Sysfs attributes under /sys/devices/platform/lis3lv02d/: -position - 3D position that the accelerometer reports. Format: "(x,y,z)" -rate - read reports the sampling rate of the accelerometer device in HZ. - write changes sampling rate of the accelerometer device. - Only values which are supported by HW are accepted. -selftest - performs selftest for the chip as specified by chip manufacturer. - -This driver also provides an absolute input class device, allowing -the laptop to act as a pinball machine-esque joystick. Joystick device can be -calibrated. Joystick device can be in two different modes. -By default output values are scaled between -32768 .. 32767. In joystick raw -mode, joystick and sysfs position entry have the same scale. There can be -small difference due to input system fuzziness feature. -Events are also available as input event device. - -Selftest is meant only for hardware diagnostic purposes. It is not meant to be -used during normal operations. Position data is not corrupted during selftest -but interrupt behaviour is not guaranteed to work reliably. In test mode, the -sensing element is internally moved little bit. Selftest measures difference -between normal mode and test mode. Chip specifications tell the acceptance -limit for each type of the chip. Limits are provided via platform data -to allow adjustment of the limits without a change to the actual driver. -Seltest returns either "OK x y z" or "FAIL x y z" where x, y and z are -measured difference between modes. Axes are not remapped in selftest mode. -Measurement values are provided to help HW diagnostic applications to make -final decision. - -On HP laptops, if the led infrastructure is activated, support for a led -indicating disk protection will be provided as /sys/class/leds/hp::hddprotect. - -Another feature of the driver is misc device called "freefall" that -acts similar to /dev/rtc and reacts on free-fall interrupts received -from the device. It supports blocking operations, poll/select and -fasync operation modes. You must read 1 bytes from the device. The -result is number of free-fall interrupts since the last successful -read (or 255 if number of interrupts would not fit). See the hpfall.c -file for an example on using the device. - - -Axes orientation ----------------- - -For better compatibility between the various laptops. The values reported by -the accelerometer are converted into a "standard" organisation of the axes -(aka "can play neverball out of the box"): - * When the laptop is horizontal the position reported is about 0 for X and Y - and a positive value for Z - * If the left side is elevated, X increases (becomes positive) - * If the front side (where the touchpad is) is elevated, Y decreases - (becomes negative) - * If the laptop is put upside-down, Z becomes negative - -If your laptop model is not recognized (cf "dmesg"), you can send an -email to the maintainer to add it to the database. When reporting a new -laptop, please include the output of "dmidecode" plus the value of -/sys/devices/platform/lis3lv02d/position in these four cases. - -Q&A ---- - -Q: How do I safely simulate freefall? I have an HP "portable -workstation" which has about 3.5kg and a plastic case, so letting it -fall to the ground is out of question... - -A: The sensor is pretty sensitive, so your hands can do it. Lift it -into free space, follow the fall with your hands for like 10 -centimeters. That should be enough to trigger the detection. diff --git a/Documentation/misc-devices/lis3lv02d b/Documentation/misc-devices/lis3lv02d new file mode 100644 index 000000000000..f1a4ec840f86 --- /dev/null +++ b/Documentation/misc-devices/lis3lv02d @@ -0,0 +1,92 @@ +Kernel driver lis3lv02d +======================= + +Supported chips: + + * STMicroelectronics LIS3LV02DL, LIS3LV02DQ (12 bits precision) + * STMicroelectronics LIS302DL, LIS3L02DQ, LIS331DL (8 bits) + +Authors: + Yan Burman + Eric Piel + + +Description +----------- + +This driver provides support for the accelerometer found in various HP laptops +sporting the feature officially called "HP Mobile Data Protection System 3D" or +"HP 3D DriveGuard". It detects automatically laptops with this sensor. Known +models (full list can be found in drivers/platform/x86/hp_accel.c) will have +their axis automatically oriented on standard way (eg: you can directly play +neverball). The accelerometer data is readable via +/sys/devices/platform/lis3lv02d. Reported values are scaled +to mg values (1/1000th of earth gravity). + +Sysfs attributes under /sys/devices/platform/lis3lv02d/: +position - 3D position that the accelerometer reports. Format: "(x,y,z)" +rate - read reports the sampling rate of the accelerometer device in HZ. + write changes sampling rate of the accelerometer device. + Only values which are supported by HW are accepted. +selftest - performs selftest for the chip as specified by chip manufacturer. + +This driver also provides an absolute input class device, allowing +the laptop to act as a pinball machine-esque joystick. Joystick device can be +calibrated. Joystick device can be in two different modes. +By default output values are scaled between -32768 .. 32767. In joystick raw +mode, joystick and sysfs position entry have the same scale. There can be +small difference due to input system fuzziness feature. +Events are also available as input event device. + +Selftest is meant only for hardware diagnostic purposes. It is not meant to be +used during normal operations. Position data is not corrupted during selftest +but interrupt behaviour is not guaranteed to work reliably. In test mode, the +sensing element is internally moved little bit. Selftest measures difference +between normal mode and test mode. Chip specifications tell the acceptance +limit for each type of the chip. Limits are provided via platform data +to allow adjustment of the limits without a change to the actual driver. +Seltest returns either "OK x y z" or "FAIL x y z" where x, y and z are +measured difference between modes. Axes are not remapped in selftest mode. +Measurement values are provided to help HW diagnostic applications to make +final decision. + +On HP laptops, if the led infrastructure is activated, support for a led +indicating disk protection will be provided as /sys/class/leds/hp::hddprotect. + +Another feature of the driver is misc device called "freefall" that +acts similar to /dev/rtc and reacts on free-fall interrupts received +from the device. It supports blocking operations, poll/select and +fasync operation modes. You must read 1 bytes from the device. The +result is number of free-fall interrupts since the last successful +read (or 255 if number of interrupts would not fit). See the hpfall.c +file for an example on using the device. + + +Axes orientation +---------------- + +For better compatibility between the various laptops. The values reported by +the accelerometer are converted into a "standard" organisation of the axes +(aka "can play neverball out of the box"): + * When the laptop is horizontal the position reported is about 0 for X and Y + and a positive value for Z + * If the left side is elevated, X increases (becomes positive) + * If the front side (where the touchpad is) is elevated, Y decreases + (becomes negative) + * If the laptop is put upside-down, Z becomes negative + +If your laptop model is not recognized (cf "dmesg"), you can send an +email to the maintainer to add it to the database. When reporting a new +laptop, please include the output of "dmidecode" plus the value of +/sys/devices/platform/lis3lv02d/position in these four cases. + +Q&A +--- + +Q: How do I safely simulate freefall? I have an HP "portable +workstation" which has about 3.5kg and a plastic case, so letting it +fall to the ground is out of question... + +A: The sensor is pretty sensitive, so your hands can do it. Lift it +into free space, follow the fall with your hands for like 10 +centimeters. That should be enough to trigger the detection. diff --git a/MAINTAINERS b/MAINTAINERS index c7a41b1fe453..4edb2a800b54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3913,8 +3913,8 @@ S: Supported LIS3LV02D ACCELEROMETER DRIVER M: Eric Piel S: Maintained -F: Documentation/hwmon/lis3lv02d -F: drivers/hwmon/lis3lv02d.* +F: Documentation/misc-devices/lis3lv02d +F: drivers/misc/lis3lv02d/ LLC (802.2) M: Arnaldo Carvalho de Melo diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3f8c895417de..47621abb9a05 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2,12 +2,6 @@ # Hardware monitoring chip drivers configuration # -config SENSORS_LIS3LV02D - tristate - depends on INPUT - select INPUT_POLLDEV - default n - menuconfig HWMON tristate "Hardware Monitoring support" depends on HAS_IOMEM @@ -1221,40 +1215,6 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. -config SENSORS_LIS3_SPI - tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" - depends on !ACPI && SPI_MASTER && INPUT - select SENSORS_LIS3LV02D - default n - help - This driver provides support for the LIS3LV02Dx accelerometer connected - via SPI. The accelerometer data is readable via - /sys/devices/platform/lis3lv02d. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - This driver can also be built as modules. If so, the core module - will be called lis3lv02d and a specific module for the SPI transport - is called lis3lv02d_spi. - -config SENSORS_LIS3_I2C - tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)" - depends on I2C && INPUT - select SENSORS_LIS3LV02D - default n - help - This driver provides support for the LIS3LV02Dx accelerometer connected - via I2C. The accelerometer data is readable via - /sys/devices/platform/lis3lv02d. - - This driver also provides an absolute input class device, allowing - the device to act as a pinball machine-esque joystick. - - This driver can also be built as modules. If so, the core module - will be called lis3lv02d and a specific module for the I2C transport - is called lis3lv02d_i2c. - config SENSORS_APPLESMC tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" depends on INPUT && X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 55ba906def74..c068f82082cd 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -5,9 +5,6 @@ obj-$(CONFIG_HWMON) += hwmon.o obj-$(CONFIG_HWMON_VID) += hwmon-vid.o -# Helper drivers -obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o - # APCI drivers obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o @@ -66,8 +63,6 @@ obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o -obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d_spi.o -obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d_i2c.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM73) += lm73.o diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c deleted file mode 100644 index d805e8e57967..000000000000 --- a/drivers/hwmon/lis3lv02d.c +++ /dev/null @@ -1,1000 +0,0 @@ -/* - * lis3lv02d.c - ST LIS3LV02DL accelerometer driver - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008-2009 Pavel Machek - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "lis3lv02d.h" - -#define DRIVER_NAME "lis3lv02d" - -/* joystick device poll interval in milliseconds */ -#define MDPS_POLL_INTERVAL 50 -#define MDPS_POLL_MIN 0 -#define MDPS_POLL_MAX 2000 - -#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ - -#define SELFTEST_OK 0 -#define SELFTEST_FAIL -1 -#define SELFTEST_IRQ -2 - -#define IRQ_LINE0 0 -#define IRQ_LINE1 1 - -/* - * The sensor can also generate interrupts (DRDY) but it's pretty pointless - * because they are generated even if the data do not change. So it's better - * to keep the interrupt for the free-fall event. The values are updated at - * 40Hz (at the lowest frequency), but as it can be pretty time consuming on - * some low processor, we poll the sensor only at 20Hz... enough for the - * joystick. - */ - -#define LIS3_PWRON_DELAY_WAI_12B (5000) -#define LIS3_PWRON_DELAY_WAI_8B (3000) - -/* - * LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG - * LIS302D spec says: 18 mG / digit - * LIS3_ACCURACY is used to increase accuracy of the intermediate - * calculation results. - */ -#define LIS3_ACCURACY 1024 -/* Sensitivity values for -2G +2G scale */ -#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024) -#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY) - -#define LIS3_DEFAULT_FUZZ_12B 3 -#define LIS3_DEFAULT_FLAT_12B 3 -#define LIS3_DEFAULT_FUZZ_8B 1 -#define LIS3_DEFAULT_FLAT_8B 1 - -struct lis3lv02d lis3_dev = { - .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), -}; - -EXPORT_SYMBOL_GPL(lis3_dev); - -/* just like param_set_int() but does sanity-check so that it won't point - * over the axis array size - */ -static int param_set_axis(const char *val, const struct kernel_param *kp) -{ - int ret = param_set_int(val, kp); - if (!ret) { - int val = *(int *)kp->arg; - if (val < 0) - val = -val; - if (!val || val > 3) - return -EINVAL; - } - return ret; -} - -static struct kernel_param_ops param_ops_axis = { - .set = param_set_axis, - .get = param_get_int, -}; - -module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644); -MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions"); - -static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) -{ - s8 lo; - if (lis3->read(lis3, reg, &lo) < 0) - return 0; - - return lo; -} - -static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg) -{ - u8 lo, hi; - - lis3->read(lis3, reg - 1, &lo); - lis3->read(lis3, reg, &hi); - /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ - return (s16)((hi << 8) | lo); -} - -/** - * lis3lv02d_get_axis - For the given axis, give the value converted - * @axis: 1,2,3 - can also be negative - * @hw_values: raw values returned by the hardware - * - * Returns the converted value. - */ -static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) -{ - if (axis > 0) - return hw_values[axis - 1]; - else - return -hw_values[-axis - 1]; -} - -/** - * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer - * @lis3: pointer to the device struct - * @x: where to store the X axis value - * @y: where to store the Y axis value - * @z: where to store the Z axis value - * - * Note that 40Hz input device can eat up about 10% CPU at 800MHZ - */ -static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) -{ - int position[3]; - int i; - - if (lis3->blkread) { - if (lis3_dev.whoami == WAI_12B) { - u16 data[3]; - lis3->blkread(lis3, OUTX_L, 6, (u8 *)data); - for (i = 0; i < 3; i++) - position[i] = (s16)le16_to_cpu(data[i]); - } else { - u8 data[5]; - /* Data: x, dummy, y, dummy, z */ - lis3->blkread(lis3, OUTX, 5, data); - for (i = 0; i < 3; i++) - position[i] = (s8)data[i * 2]; - } - } else { - position[0] = lis3->read_data(lis3, OUTX); - position[1] = lis3->read_data(lis3, OUTY); - position[2] = lis3->read_data(lis3, OUTZ); - } - - for (i = 0; i < 3; i++) - position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; - - *x = lis3lv02d_get_axis(lis3->ac.x, position); - *y = lis3lv02d_get_axis(lis3->ac.y, position); - *z = lis3lv02d_get_axis(lis3->ac.z, position); -} - -/* conversion btw sampling rate and the register values */ -static int lis3_12_rates[4] = {40, 160, 640, 2560}; -static int lis3_8_rates[2] = {100, 400}; -static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000}; - -/* ODR is Output Data Rate */ -static int lis3lv02d_get_odr(void) -{ - u8 ctrl; - int shift; - - lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); - ctrl &= lis3_dev.odr_mask; - shift = ffs(lis3_dev.odr_mask) - 1; - return lis3_dev.odrs[(ctrl >> shift)]; -} - -static int lis3lv02d_set_odr(int rate) -{ - u8 ctrl; - int i, len, shift; - - if (!rate) - return -EINVAL; - - lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); - ctrl &= ~lis3_dev.odr_mask; - len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ - shift = ffs(lis3_dev.odr_mask) - 1; - - for (i = 0; i < len; i++) - if (lis3_dev.odrs[i] == rate) { - lis3_dev.write(&lis3_dev, CTRL_REG1, - ctrl | (i << shift)); - return 0; - } - return -EINVAL; -} - -static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) -{ - u8 ctlreg, reg; - s16 x, y, z; - u8 selftest; - int ret; - u8 ctrl_reg_data; - unsigned char irq_cfg; - - mutex_lock(&lis3->mutex); - - irq_cfg = lis3->irq_cfg; - if (lis3_dev.whoami == WAI_8B) { - lis3->data_ready_count[IRQ_LINE0] = 0; - lis3->data_ready_count[IRQ_LINE1] = 0; - - /* Change interrupt cfg to data ready for selftest */ - atomic_inc(&lis3_dev.wake_thread); - lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY; - lis3->read(lis3, CTRL_REG3, &ctrl_reg_data); - lis3->write(lis3, CTRL_REG3, (ctrl_reg_data & - ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) | - (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY)); - } - - if (lis3_dev.whoami == WAI_3DC) { - ctlreg = CTRL_REG4; - selftest = CTRL4_ST0; - } else { - ctlreg = CTRL_REG1; - if (lis3_dev.whoami == WAI_12B) - selftest = CTRL1_ST; - else - selftest = CTRL1_STP; - } - - lis3->read(lis3, ctlreg, ®); - lis3->write(lis3, ctlreg, (reg | selftest)); - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - - /* Read directly to avoid axis remap */ - x = lis3->read_data(lis3, OUTX); - y = lis3->read_data(lis3, OUTY); - z = lis3->read_data(lis3, OUTZ); - - /* back to normal settings */ - lis3->write(lis3, ctlreg, reg); - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - - results[0] = x - lis3->read_data(lis3, OUTX); - results[1] = y - lis3->read_data(lis3, OUTY); - results[2] = z - lis3->read_data(lis3, OUTZ); - - ret = 0; - - if (lis3_dev.whoami == WAI_8B) { - /* Restore original interrupt configuration */ - atomic_dec(&lis3_dev.wake_thread); - lis3->write(lis3, CTRL_REG3, ctrl_reg_data); - lis3->irq_cfg = irq_cfg; - - if ((irq_cfg & LIS3_IRQ1_MASK) && - lis3->data_ready_count[IRQ_LINE0] < 2) { - ret = SELFTEST_IRQ; - goto fail; - } - - if ((irq_cfg & LIS3_IRQ2_MASK) && - lis3->data_ready_count[IRQ_LINE1] < 2) { - ret = SELFTEST_IRQ; - goto fail; - } - } - - if (lis3->pdata) { - int i; - for (i = 0; i < 3; i++) { - /* Check against selftest acceptance limits */ - if ((results[i] < lis3->pdata->st_min_limits[i]) || - (results[i] > lis3->pdata->st_max_limits[i])) { - ret = SELFTEST_FAIL; - goto fail; - } - } - } - - /* test passed */ -fail: - mutex_unlock(&lis3->mutex); - return ret; -} - -/* - * Order of registers in the list affects to order of the restore process. - * Perhaps it is a good idea to set interrupt enable register as a last one - * after all other configurations - */ -static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, - FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, - CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, - CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, - CTRL_REG1, CTRL_REG2, CTRL_REG3}; - -static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, - FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, - DD_THSE_L, DD_THSE_H, - CTRL_REG1, CTRL_REG3, CTRL_REG2}; - -static inline void lis3_context_save(struct lis3lv02d *lis3) -{ - int i; - for (i = 0; i < lis3->regs_size; i++) - lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); - lis3->regs_stored = true; -} - -static inline void lis3_context_restore(struct lis3lv02d *lis3) -{ - int i; - if (lis3->regs_stored) - for (i = 0; i < lis3->regs_size; i++) - lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); -} - -void lis3lv02d_poweroff(struct lis3lv02d *lis3) -{ - if (lis3->reg_ctrl) - lis3_context_save(lis3); - /* disable X,Y,Z axis and power down */ - lis3->write(lis3, CTRL_REG1, 0x00); - if (lis3->reg_ctrl) - lis3->reg_ctrl(lis3, LIS3_REG_OFF); -} -EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); - -void lis3lv02d_poweron(struct lis3lv02d *lis3) -{ - u8 reg; - - lis3->init(lis3); - - /* - * Common configuration - * BDU: (12 bits sensors only) LSB and MSB values are not updated until - * both have been read. So the value read will always be correct. - * Set BOOT bit to refresh factory tuning values. - */ - lis3->read(lis3, CTRL_REG2, ®); - if (lis3->whoami == WAI_12B) - reg |= CTRL2_BDU | CTRL2_BOOT; - else - reg |= CTRL2_BOOT_8B; - lis3->write(lis3, CTRL_REG2, reg); - - /* LIS3 power on delay is quite long */ - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - - if (lis3->reg_ctrl) - lis3_context_restore(lis3); -} -EXPORT_SYMBOL_GPL(lis3lv02d_poweron); - - -static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) -{ - int x, y, z; - - mutex_lock(&lis3_dev.mutex); - lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); - input_report_abs(pidev->input, ABS_X, x); - input_report_abs(pidev->input, ABS_Y, y); - input_report_abs(pidev->input, ABS_Z, z); - input_sync(pidev->input); - mutex_unlock(&lis3_dev.mutex); -} - -static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) -{ - if (lis3_dev.pm_dev) - pm_runtime_get_sync(lis3_dev.pm_dev); - - if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev) - atomic_set(&lis3_dev.wake_thread, 1); - /* - * Update coordinates for the case where poll interval is 0 and - * the chip in running purely under interrupt control - */ - lis3lv02d_joystick_poll(pidev); -} - -static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) -{ - atomic_set(&lis3_dev.wake_thread, 0); - if (lis3_dev.pm_dev) - pm_runtime_put(lis3_dev.pm_dev); -} - -static irqreturn_t lis302dl_interrupt(int irq, void *dummy) -{ - if (!test_bit(0, &lis3_dev.misc_opened)) - goto out; - - /* - * Be careful: on some HP laptops the bios force DD when on battery and - * the lid is closed. This leads to interrupts as soon as a little move - * is done. - */ - atomic_inc(&lis3_dev.count); - - wake_up_interruptible(&lis3_dev.misc_wait); - kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); -out: - if (atomic_read(&lis3_dev.wake_thread)) - return IRQ_WAKE_THREAD; - return IRQ_HANDLED; -} - -static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) -{ - struct input_dev *dev = lis3->idev->input; - u8 click_src; - - mutex_lock(&lis3->mutex); - lis3->read(lis3, CLICK_SRC, &click_src); - - if (click_src & CLICK_SINGLE_X) { - input_report_key(dev, lis3->mapped_btns[0], 1); - input_report_key(dev, lis3->mapped_btns[0], 0); - } - - if (click_src & CLICK_SINGLE_Y) { - input_report_key(dev, lis3->mapped_btns[1], 1); - input_report_key(dev, lis3->mapped_btns[1], 0); - } - - if (click_src & CLICK_SINGLE_Z) { - input_report_key(dev, lis3->mapped_btns[2], 1); - input_report_key(dev, lis3->mapped_btns[2], 0); - } - input_sync(dev); - mutex_unlock(&lis3->mutex); -} - -static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index) -{ - int dummy; - - /* Dummy read to ack interrupt */ - lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy); - lis3->data_ready_count[index]++; -} - -static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) -{ - struct lis3lv02d *lis3 = data; - u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK; - - if (irq_cfg == LIS3_IRQ1_CLICK) - lis302dl_interrupt_handle_click(lis3); - else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY)) - lis302dl_data_ready(lis3, IRQ_LINE0); - else - lis3lv02d_joystick_poll(lis3->idev); - - return IRQ_HANDLED; -} - -static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) -{ - struct lis3lv02d *lis3 = data; - u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK; - - if (irq_cfg == LIS3_IRQ2_CLICK) - lis302dl_interrupt_handle_click(lis3); - else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY)) - lis302dl_data_ready(lis3, IRQ_LINE1); - else - lis3lv02d_joystick_poll(lis3->idev); - - return IRQ_HANDLED; -} - -static int lis3lv02d_misc_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &lis3_dev.misc_opened)) - return -EBUSY; /* already open */ - - if (lis3_dev.pm_dev) - pm_runtime_get_sync(lis3_dev.pm_dev); - - atomic_set(&lis3_dev.count, 0); - return 0; -} - -static int lis3lv02d_misc_release(struct inode *inode, struct file *file) -{ - fasync_helper(-1, file, 0, &lis3_dev.async_queue); - clear_bit(0, &lis3_dev.misc_opened); /* release the device */ - if (lis3_dev.pm_dev) - pm_runtime_put(lis3_dev.pm_dev); - return 0; -} - -static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, - size_t count, loff_t *pos) -{ - DECLARE_WAITQUEUE(wait, current); - u32 data; - unsigned char byte_data; - ssize_t retval = 1; - - if (count < 1) - return -EINVAL; - - add_wait_queue(&lis3_dev.misc_wait, &wait); - while (true) { - set_current_state(TASK_INTERRUPTIBLE); - data = atomic_xchg(&lis3_dev.count, 0); - if (data) - break; - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - goto out; - } - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - goto out; - } - - schedule(); - } - - if (data < 255) - byte_data = data; - else - byte_data = 255; - - /* make sure we are not going into copy_to_user() with - * TASK_INTERRUPTIBLE state */ - set_current_state(TASK_RUNNING); - if (copy_to_user(buf, &byte_data, sizeof(byte_data))) - retval = -EFAULT; - -out: - __set_current_state(TASK_RUNNING); - remove_wait_queue(&lis3_dev.misc_wait, &wait); - - return retval; -} - -static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &lis3_dev.misc_wait, wait); - if (atomic_read(&lis3_dev.count)) - return POLLIN | POLLRDNORM; - return 0; -} - -static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) -{ - return fasync_helper(fd, file, on, &lis3_dev.async_queue); -} - -static const struct file_operations lis3lv02d_misc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = lis3lv02d_misc_read, - .open = lis3lv02d_misc_open, - .release = lis3lv02d_misc_release, - .poll = lis3lv02d_misc_poll, - .fasync = lis3lv02d_misc_fasync, -}; - -static struct miscdevice lis3lv02d_misc_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "freefall", - .fops = &lis3lv02d_misc_fops, -}; - -int lis3lv02d_joystick_enable(void) -{ - struct input_dev *input_dev; - int err; - int max_val, fuzz, flat; - int btns[] = {BTN_X, BTN_Y, BTN_Z}; - - if (lis3_dev.idev) - return -EINVAL; - - lis3_dev.idev = input_allocate_polled_device(); - if (!lis3_dev.idev) - return -ENOMEM; - - lis3_dev.idev->poll = lis3lv02d_joystick_poll; - lis3_dev.idev->open = lis3lv02d_joystick_open; - lis3_dev.idev->close = lis3lv02d_joystick_close; - lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; - lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; - lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; - input_dev = lis3_dev.idev->input; - - input_dev->name = "ST LIS3LV02DL Accelerometer"; - input_dev->phys = DRIVER_NAME "/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0; - input_dev->dev.parent = &lis3_dev.pdev->dev; - - set_bit(EV_ABS, input_dev->evbit); - max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY; - if (lis3_dev.whoami == WAI_12B) { - fuzz = LIS3_DEFAULT_FUZZ_12B; - flat = LIS3_DEFAULT_FLAT_12B; - } else { - fuzz = LIS3_DEFAULT_FUZZ_8B; - flat = LIS3_DEFAULT_FLAT_8B; - } - fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY; - flat = (flat * lis3_dev.scale) / LIS3_ACCURACY; - - input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat); - input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); - input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); - - lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns); - lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns); - lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns); - - err = input_register_polled_device(lis3_dev.idev); - if (err) { - input_free_polled_device(lis3_dev.idev); - lis3_dev.idev = NULL; - } - - return err; -} -EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); - -void lis3lv02d_joystick_disable(void) -{ - if (lis3_dev.irq) - free_irq(lis3_dev.irq, &lis3_dev); - if (lis3_dev.pdata && lis3_dev.pdata->irq2) - free_irq(lis3_dev.pdata->irq2, &lis3_dev); - - if (!lis3_dev.idev) - return; - - if (lis3_dev.irq) - misc_deregister(&lis3lv02d_misc_device); - input_unregister_polled_device(lis3_dev.idev); - input_free_polled_device(lis3_dev.idev); - lis3_dev.idev = NULL; -} -EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); - -/* Sysfs stuff */ -static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) -{ - /* - * SYSFS functions are fast visitors so put-call - * immediately after the get-call. However, keep - * chip running for a while and schedule delayed - * suspend. This way periodic sysfs calls doesn't - * suffer from relatively long power up time. - */ - - if (lis3->pm_dev) { - pm_runtime_get_sync(lis3->pm_dev); - pm_runtime_put_noidle(lis3->pm_dev); - pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); - } -} - -static ssize_t lis3lv02d_selftest_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - s16 values[3]; - - static const char ok[] = "OK"; - static const char fail[] = "FAIL"; - static const char irq[] = "FAIL_IRQ"; - const char *res; - - lis3lv02d_sysfs_poweron(&lis3_dev); - switch (lis3lv02d_selftest(&lis3_dev, values)) { - case SELFTEST_FAIL: - res = fail; - break; - case SELFTEST_IRQ: - res = irq; - break; - case SELFTEST_OK: - default: - res = ok; - break; - } - return sprintf(buf, "%s %d %d %d\n", res, - values[0], values[1], values[2]); -} - -static ssize_t lis3lv02d_position_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int x, y, z; - - lis3lv02d_sysfs_poweron(&lis3_dev); - mutex_lock(&lis3_dev.mutex); - lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); - mutex_unlock(&lis3_dev.mutex); - return sprintf(buf, "(%d,%d,%d)\n", x, y, z); -} - -static ssize_t lis3lv02d_rate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - lis3lv02d_sysfs_poweron(&lis3_dev); - return sprintf(buf, "%d\n", lis3lv02d_get_odr()); -} - -static ssize_t lis3lv02d_rate_set(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - unsigned long rate; - - if (strict_strtoul(buf, 0, &rate)) - return -EINVAL; - - lis3lv02d_sysfs_poweron(&lis3_dev); - if (lis3lv02d_set_odr(rate)) - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL); -static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); -static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show, - lis3lv02d_rate_set); - -static struct attribute *lis3lv02d_attributes[] = { - &dev_attr_selftest.attr, - &dev_attr_position.attr, - &dev_attr_rate.attr, - NULL -}; - -static struct attribute_group lis3lv02d_attribute_group = { - .attrs = lis3lv02d_attributes -}; - - -static int lis3lv02d_add_fs(struct lis3lv02d *lis3) -{ - lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); - if (IS_ERR(lis3->pdev)) - return PTR_ERR(lis3->pdev); - - return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); -} - -int lis3lv02d_remove_fs(struct lis3lv02d *lis3) -{ - sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); - platform_device_unregister(lis3->pdev); - if (lis3->pm_dev) { - /* Barrier after the sysfs remove */ - pm_runtime_barrier(lis3->pm_dev); - - /* SYSFS may have left chip running. Turn off if necessary */ - if (!pm_runtime_suspended(lis3->pm_dev)) - lis3lv02d_poweroff(&lis3_dev); - - pm_runtime_disable(lis3->pm_dev); - pm_runtime_set_suspended(lis3->pm_dev); - } - kfree(lis3->reg_cache); - return 0; -} -EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); - -static void lis3lv02d_8b_configure(struct lis3lv02d *dev, - struct lis3lv02d_platform_data *p) -{ - int err; - int ctrl2 = p->hipass_ctrl; - - if (p->click_flags) { - dev->write(dev, CLICK_CFG, p->click_flags); - dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit); - dev->write(dev, CLICK_LATENCY, p->click_latency); - dev->write(dev, CLICK_WINDOW, p->click_window); - dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf); - dev->write(dev, CLICK_THSY_X, - (p->click_thresh_x & 0xf) | - (p->click_thresh_y << 4)); - - if (dev->idev) { - struct input_dev *input_dev = lis3_dev.idev->input; - input_set_capability(input_dev, EV_KEY, BTN_X); - input_set_capability(input_dev, EV_KEY, BTN_Y); - input_set_capability(input_dev, EV_KEY, BTN_Z); - } - } - - if (p->wakeup_flags) { - dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); - dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); - /* pdata value + 1 to keep this backward compatible*/ - dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1); - ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/ - } - - if (p->wakeup_flags2) { - dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2); - dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f); - /* pdata value + 1 to keep this backward compatible*/ - dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1); - ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/ - } - /* Configure hipass filters */ - dev->write(dev, CTRL_REG2, ctrl2); - - if (p->irq2) { - err = request_threaded_irq(p->irq2, - NULL, - lis302dl_interrupt_thread2_8b, - IRQF_TRIGGER_RISING | IRQF_ONESHOT | - (p->irq_flags2 & IRQF_TRIGGER_MASK), - DRIVER_NAME, &lis3_dev); - if (err < 0) - pr_err("No second IRQ. Limited functionality\n"); - } -} - -/* - * Initialise the accelerometer and the various subsystems. - * Should be rather independent of the bus system. - */ -int lis3lv02d_init_device(struct lis3lv02d *dev) -{ - int err; - irq_handler_t thread_fn; - int irq_flags = 0; - - dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); - - switch (dev->whoami) { - case WAI_12B: - pr_info("12 bits sensor found\n"); - dev->read_data = lis3lv02d_read_12; - dev->mdps_max_val = 2048; - dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B; - dev->odrs = lis3_12_rates; - dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; - dev->scale = LIS3_SENSITIVITY_12B; - dev->regs = lis3_wai12_regs; - dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); - break; - case WAI_8B: - pr_info("8 bits sensor found\n"); - dev->read_data = lis3lv02d_read_8; - dev->mdps_max_val = 128; - dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; - dev->odrs = lis3_8_rates; - dev->odr_mask = CTRL1_DR; - dev->scale = LIS3_SENSITIVITY_8B; - dev->regs = lis3_wai8_regs; - dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); - break; - case WAI_3DC: - pr_info("8 bits 3DC sensor found\n"); - dev->read_data = lis3lv02d_read_8; - dev->mdps_max_val = 128; - dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; - dev->odrs = lis3_3dc_rates; - dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3; - dev->scale = LIS3_SENSITIVITY_8B; - break; - default: - pr_err("unknown sensor type 0x%X\n", dev->whoami); - return -EINVAL; - } - - dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), - sizeof(lis3_wai12_regs)), GFP_KERNEL); - - if (dev->reg_cache == NULL) { - printk(KERN_ERR DRIVER_NAME "out of memory\n"); - return -ENOMEM; - } - - mutex_init(&dev->mutex); - atomic_set(&dev->wake_thread, 0); - - lis3lv02d_add_fs(dev); - lis3lv02d_poweron(dev); - - if (dev->pm_dev) { - pm_runtime_set_active(dev->pm_dev); - pm_runtime_enable(dev->pm_dev); - } - - if (lis3lv02d_joystick_enable()) - pr_err("joystick initialization failed\n"); - - /* passing in platform specific data is purely optional and only - * used by the SPI transport layer at the moment */ - if (dev->pdata) { - struct lis3lv02d_platform_data *p = dev->pdata; - - if (dev->whoami == WAI_8B) - lis3lv02d_8b_configure(dev, p); - - irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK; - - dev->irq_cfg = p->irq_cfg; - if (p->irq_cfg) - dev->write(dev, CTRL_REG3, p->irq_cfg); - - if (p->default_rate) - lis3lv02d_set_odr(p->default_rate); - } - - /* bail if we did not get an IRQ from the bus layer */ - if (!dev->irq) { - pr_debug("No IRQ. Disabling /dev/freefall\n"); - goto out; - } - - /* - * The sensor can generate interrupts for free-fall and direction - * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep - * the things simple and _fast_ we activate it only for free-fall, so - * no need to read register (very slow with ACPI). For the same reason, - * we forbid shared interrupts. - * - * IRQF_TRIGGER_RISING seems pointless on HP laptops because the - * io-apic is not configurable (and generates a warning) but I keep it - * in case of support for other hardware. - */ - if (dev->pdata && dev->whoami == WAI_8B) - thread_fn = lis302dl_interrupt_thread1_8b; - else - thread_fn = NULL; - - err = request_threaded_irq(dev->irq, lis302dl_interrupt, - thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT | - irq_flags, - DRIVER_NAME, &lis3_dev); - - if (err < 0) { - pr_err("Cannot get IRQ\n"); - goto out; - } - - if (misc_register(&lis3lv02d_misc_device)) - pr_err("misc_register failed\n"); -out: - return 0; -} -EXPORT_SYMBOL_GPL(lis3lv02d_init_device); - -MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); -MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h deleted file mode 100644 index a1939589eb2c..000000000000 --- a/drivers/hwmon/lis3lv02d.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - * lis3lv02d.h - ST LIS3LV02DL accelerometer driver - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008-2009 Eric Piel - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include - -/* - * This driver tries to support the "digital" accelerometer chips from - * STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL, - * LIS35DE, or LIS202DL. They are very similar in terms of programming, with - * almost the same registers. In addition to differing on physical properties, - * they differ on the number of axes (2/3), precision (8/12 bits), and special - * features (freefall detection, click...). Unfortunately, not all the - * differences can be probed via a register. - * They can be connected either via I²C or SPI. - */ - -#include - -enum lis3_reg { - WHO_AM_I = 0x0F, - OFFSET_X = 0x16, - OFFSET_Y = 0x17, - OFFSET_Z = 0x18, - GAIN_X = 0x19, - GAIN_Y = 0x1A, - GAIN_Z = 0x1B, - CTRL_REG1 = 0x20, - CTRL_REG2 = 0x21, - CTRL_REG3 = 0x22, - CTRL_REG4 = 0x23, - HP_FILTER_RESET = 0x23, - STATUS_REG = 0x27, - OUTX_L = 0x28, - OUTX_H = 0x29, - OUTX = 0x29, - OUTY_L = 0x2A, - OUTY_H = 0x2B, - OUTY = 0x2B, - OUTZ_L = 0x2C, - OUTZ_H = 0x2D, - OUTZ = 0x2D, -}; - -enum lis302d_reg { - FF_WU_CFG_1 = 0x30, - FF_WU_SRC_1 = 0x31, - FF_WU_THS_1 = 0x32, - FF_WU_DURATION_1 = 0x33, - FF_WU_CFG_2 = 0x34, - FF_WU_SRC_2 = 0x35, - FF_WU_THS_2 = 0x36, - FF_WU_DURATION_2 = 0x37, - CLICK_CFG = 0x38, - CLICK_SRC = 0x39, - CLICK_THSY_X = 0x3B, - CLICK_THSZ = 0x3C, - CLICK_TIMELIMIT = 0x3D, - CLICK_LATENCY = 0x3E, - CLICK_WINDOW = 0x3F, -}; - -enum lis3lv02d_reg { - FF_WU_CFG = 0x30, - FF_WU_SRC = 0x31, - FF_WU_ACK = 0x32, - FF_WU_THS_L = 0x34, - FF_WU_THS_H = 0x35, - FF_WU_DURATION = 0x36, - DD_CFG = 0x38, - DD_SRC = 0x39, - DD_ACK = 0x3A, - DD_THSI_L = 0x3C, - DD_THSI_H = 0x3D, - DD_THSE_L = 0x3E, - DD_THSE_H = 0x3F, -}; - -enum lis3_who_am_i { - WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */ - WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ - WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ - WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ -}; - -enum lis3lv02d_ctrl1_12b { - CTRL1_Xen = 0x01, - CTRL1_Yen = 0x02, - CTRL1_Zen = 0x04, - CTRL1_ST = 0x08, - CTRL1_DF0 = 0x10, - CTRL1_DF1 = 0x20, - CTRL1_PD0 = 0x40, - CTRL1_PD1 = 0x80, -}; - -/* Delta to ctrl1_12b version */ -enum lis3lv02d_ctrl1_8b { - CTRL1_STM = 0x08, - CTRL1_STP = 0x10, - CTRL1_FS = 0x20, - CTRL1_PD = 0x40, - CTRL1_DR = 0x80, -}; - -enum lis3lv02d_ctrl1_3dc { - CTRL1_ODR0 = 0x10, - CTRL1_ODR1 = 0x20, - CTRL1_ODR2 = 0x40, - CTRL1_ODR3 = 0x80, -}; - -enum lis3lv02d_ctrl2 { - CTRL2_DAS = 0x01, - CTRL2_SIM = 0x02, - CTRL2_DRDY = 0x04, - CTRL2_IEN = 0x08, - CTRL2_BOOT = 0x10, - CTRL2_BLE = 0x20, - CTRL2_BDU = 0x40, /* Block Data Update */ - CTRL2_FS = 0x80, /* Full Scale selection */ -}; - -enum lis3lv02d_ctrl4_3dc { - CTRL4_SIM = 0x01, - CTRL4_ST0 = 0x02, - CTRL4_ST1 = 0x04, - CTRL4_FS0 = 0x10, - CTRL4_FS1 = 0x20, -}; - -enum lis302d_ctrl2 { - HP_FF_WU2 = 0x08, - HP_FF_WU1 = 0x04, - CTRL2_BOOT_8B = 0x40, -}; - -enum lis3lv02d_ctrl3 { - CTRL3_CFS0 = 0x01, - CTRL3_CFS1 = 0x02, - CTRL3_FDS = 0x10, - CTRL3_HPFF = 0x20, - CTRL3_HPDD = 0x40, - CTRL3_ECK = 0x80, -}; - -enum lis3lv02d_status_reg { - STATUS_XDA = 0x01, - STATUS_YDA = 0x02, - STATUS_ZDA = 0x04, - STATUS_XYZDA = 0x08, - STATUS_XOR = 0x10, - STATUS_YOR = 0x20, - STATUS_ZOR = 0x40, - STATUS_XYZOR = 0x80, -}; - -enum lis3lv02d_ff_wu_cfg { - FF_WU_CFG_XLIE = 0x01, - FF_WU_CFG_XHIE = 0x02, - FF_WU_CFG_YLIE = 0x04, - FF_WU_CFG_YHIE = 0x08, - FF_WU_CFG_ZLIE = 0x10, - FF_WU_CFG_ZHIE = 0x20, - FF_WU_CFG_LIR = 0x40, - FF_WU_CFG_AOI = 0x80, -}; - -enum lis3lv02d_ff_wu_src { - FF_WU_SRC_XL = 0x01, - FF_WU_SRC_XH = 0x02, - FF_WU_SRC_YL = 0x04, - FF_WU_SRC_YH = 0x08, - FF_WU_SRC_ZL = 0x10, - FF_WU_SRC_ZH = 0x20, - FF_WU_SRC_IA = 0x40, -}; - -enum lis3lv02d_dd_cfg { - DD_CFG_XLIE = 0x01, - DD_CFG_XHIE = 0x02, - DD_CFG_YLIE = 0x04, - DD_CFG_YHIE = 0x08, - DD_CFG_ZLIE = 0x10, - DD_CFG_ZHIE = 0x20, - DD_CFG_LIR = 0x40, - DD_CFG_IEND = 0x80, -}; - -enum lis3lv02d_dd_src { - DD_SRC_XL = 0x01, - DD_SRC_XH = 0x02, - DD_SRC_YL = 0x04, - DD_SRC_YH = 0x08, - DD_SRC_ZL = 0x10, - DD_SRC_ZH = 0x20, - DD_SRC_IA = 0x40, -}; - -enum lis3lv02d_click_src_8b { - CLICK_SINGLE_X = 0x01, - CLICK_DOUBLE_X = 0x02, - CLICK_SINGLE_Y = 0x04, - CLICK_DOUBLE_Y = 0x08, - CLICK_SINGLE_Z = 0x10, - CLICK_DOUBLE_Z = 0x20, - CLICK_IA = 0x40, -}; - -enum lis3lv02d_reg_state { - LIS3_REG_OFF = 0x00, - LIS3_REG_ON = 0x01, -}; - -union axis_conversion { - struct { - int x, y, z; - }; - int as_array[3]; - -}; - -struct lis3lv02d { - void *bus_priv; /* used by the bus layer only */ - struct device *pm_dev; /* for pm_runtime purposes */ - int (*init) (struct lis3lv02d *lis3); - int (*write) (struct lis3lv02d *lis3, int reg, u8 val); - int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); - int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret); - int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); - - int *odrs; /* Supported output data rates */ - u8 *regs; /* Regs to store / restore */ - int regs_size; - u8 *reg_cache; - bool regs_stored; - u8 odr_mask; /* ODR bit mask */ - u8 whoami; /* indicates measurement precision */ - s16 (*read_data) (struct lis3lv02d *lis3, int reg); - int mdps_max_val; - int pwron_delay; - int scale; /* - * relationship between 1 LBS and mG - * (1/1000th of earth gravity) - */ - - struct input_polled_dev *idev; /* input device */ - struct platform_device *pdev; /* platform device */ - struct regulator_bulk_data regulators[2]; - atomic_t count; /* interrupt count after last read */ - union axis_conversion ac; /* hw -> logical axis */ - int mapped_btns[3]; - - u32 irq; /* IRQ number */ - struct fasync_struct *async_queue; /* queue for the misc device */ - wait_queue_head_t misc_wait; /* Wait queue for the misc device */ - unsigned long misc_opened; /* bit0: whether the device is open */ - int data_ready_count[2]; - atomic_t wake_thread; - unsigned char irq_cfg; - - struct lis3lv02d_platform_data *pdata; /* for passing board config */ - struct mutex mutex; /* Serialize poll and selftest */ -}; - -int lis3lv02d_init_device(struct lis3lv02d *lis3); -int lis3lv02d_joystick_enable(void); -void lis3lv02d_joystick_disable(void); -void lis3lv02d_poweroff(struct lis3lv02d *lis3); -void lis3lv02d_poweron(struct lis3lv02d *lis3); -int lis3lv02d_remove_fs(struct lis3lv02d *lis3); - -extern struct lis3lv02d lis3_dev; diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c deleted file mode 100644 index 8853afce85ce..000000000000 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * drivers/hwmon/lis3lv02d_i2c.c - * - * Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer. - * Driver is based on corresponding SPI driver written by Daniel Mack - * (lis3lv02d_spi.c (C) 2009 Daniel Mack ). - * - * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Samu Onkalo - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include "lis3lv02d.h" - -#define DRV_NAME "lis3lv02d_i2c" - -static const char reg_vdd[] = "Vdd"; -static const char reg_vdd_io[] = "Vdd_IO"; - -static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) -{ - int ret; - if (state == LIS3_REG_OFF) { - ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), - lis3->regulators); - } else { - ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), - lis3->regulators); - /* Chip needs time to wakeup. Not mentioned in datasheet */ - usleep_range(10000, 20000); - } - return ret; -} - -static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) -{ - struct i2c_client *c = lis3->bus_priv; - return i2c_smbus_write_byte_data(c, reg, value); -} - -static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) -{ - struct i2c_client *c = lis3->bus_priv; - *v = i2c_smbus_read_byte_data(c, reg); - return 0; -} - -static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, - u8 *v) -{ - struct i2c_client *c = lis3->bus_priv; - reg |= (1 << 7); /* 7th bit enables address auto incrementation */ - return i2c_smbus_read_i2c_block_data(c, reg, len, v); -} - -static int lis3_i2c_init(struct lis3lv02d *lis3) -{ - u8 reg; - int ret; - - if (lis3->reg_ctrl) - lis3_reg_ctrl(lis3, LIS3_REG_ON); - - lis3->read(lis3, WHO_AM_I, ®); - if (reg != lis3->whoami) - printk(KERN_ERR "lis3: power on failure\n"); - - /* power up the device */ - ret = lis3->read(lis3, CTRL_REG1, ®); - if (ret < 0) - return ret; - - reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; - return lis3->write(lis3, CTRL_REG1, reg); -} - -/* Default axis mapping but it can be overwritten by platform data */ -static union axis_conversion lis3lv02d_axis_map = - { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; - -static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int ret = 0; - struct lis3lv02d_platform_data *pdata = client->dev.platform_data; - - if (pdata) { - /* Regulator control is optional */ - if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) - lis3_dev.reg_ctrl = lis3_reg_ctrl; - - if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && - (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_I2C_BLOCK))) - lis3_dev.blkread = lis3_i2c_blockread; - - if (pdata->axis_x) - lis3lv02d_axis_map.x = pdata->axis_x; - - if (pdata->axis_y) - lis3lv02d_axis_map.y = pdata->axis_y; - - if (pdata->axis_z) - lis3lv02d_axis_map.z = pdata->axis_z; - - if (pdata->setup_resources) - ret = pdata->setup_resources(); - - if (ret) - goto fail; - } - - if (lis3_dev.reg_ctrl) { - lis3_dev.regulators[0].supply = reg_vdd; - lis3_dev.regulators[1].supply = reg_vdd_io; - ret = regulator_bulk_get(&client->dev, - ARRAY_SIZE(lis3_dev.regulators), - lis3_dev.regulators); - if (ret < 0) - goto fail; - } - - lis3_dev.pdata = pdata; - lis3_dev.bus_priv = client; - lis3_dev.init = lis3_i2c_init; - lis3_dev.read = lis3_i2c_read; - lis3_dev.write = lis3_i2c_write; - lis3_dev.irq = client->irq; - lis3_dev.ac = lis3lv02d_axis_map; - lis3_dev.pm_dev = &client->dev; - - i2c_set_clientdata(client, &lis3_dev); - - /* Provide power over the init call */ - if (lis3_dev.reg_ctrl) - lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); - - ret = lis3lv02d_init_device(&lis3_dev); - - if (lis3_dev.reg_ctrl) - lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); - - if (ret == 0) - return 0; -fail: - if (pdata && pdata->release_resources) - pdata->release_resources(); - return ret; -} - -static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) -{ - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - struct lis3lv02d_platform_data *pdata = client->dev.platform_data; - - if (pdata && pdata->release_resources) - pdata->release_resources(); - - lis3lv02d_joystick_disable(); - lis3lv02d_remove_fs(&lis3_dev); - - if (lis3_dev.reg_ctrl) - regulator_bulk_free(ARRAY_SIZE(lis3->regulators), - lis3_dev.regulators); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int lis3lv02d_i2c_suspend(struct device *dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - - if (!lis3->pdata || !lis3->pdata->wakeup_flags) - lis3lv02d_poweroff(lis3); - return 0; -} - -static int lis3lv02d_i2c_resume(struct device *dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - - /* - * pm_runtime documentation says that devices should always - * be powered on at resume. Pm_runtime turns them off after system - * wide resume is complete. - */ - if (!lis3->pdata || !lis3->pdata->wakeup_flags || - pm_runtime_suspended(dev)) - lis3lv02d_poweron(lis3); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -#ifdef CONFIG_PM_RUNTIME -static int lis3_i2c_runtime_suspend(struct device *dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - - lis3lv02d_poweroff(lis3); - return 0; -} - -static int lis3_i2c_runtime_resume(struct device *dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - - lis3lv02d_poweron(lis3); - return 0; -} -#endif /* CONFIG_PM_RUNTIME */ - -static const struct i2c_device_id lis3lv02d_id[] = { - {"lis3lv02d", 0 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); - -static const struct dev_pm_ops lis3_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, - lis3lv02d_i2c_resume) - SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, - lis3_i2c_runtime_resume, - NULL) -}; - -static struct i2c_driver lis3lv02d_i2c_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .pm = &lis3_pm_ops, - }, - .probe = lis3lv02d_i2c_probe, - .remove = __devexit_p(lis3lv02d_i2c_remove), - .id_table = lis3lv02d_id, -}; - -static int __init lis3lv02d_init(void) -{ - return i2c_add_driver(&lis3lv02d_i2c_driver); -} - -static void __exit lis3lv02d_exit(void) -{ - i2c_del_driver(&lis3lv02d_i2c_driver); -} - -MODULE_AUTHOR("Nokia Corporation"); -MODULE_DESCRIPTION("lis3lv02d I2C interface"); -MODULE_LICENSE("GPL"); - -module_init(lis3lv02d_init); -module_exit(lis3lv02d_exit); diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c deleted file mode 100644 index c1f8a8fbf694..000000000000 --- a/drivers/hwmon/lis3lv02d_spi.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * lis3lv02d_spi - SPI glue layer for lis3lv02d - * - * Copyright (c) 2009 Daniel Mack - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * publishhed by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lis3lv02d.h" - -#define DRV_NAME "lis3lv02d_spi" -#define LIS3_SPI_READ 0x80 - -static int lis3_spi_read(struct lis3lv02d *lis3, int reg, u8 *v) -{ - struct spi_device *spi = lis3->bus_priv; - int ret = spi_w8r8(spi, reg | LIS3_SPI_READ); - if (ret < 0) - return -EINVAL; - - *v = (u8) ret; - return 0; -} - -static int lis3_spi_write(struct lis3lv02d *lis3, int reg, u8 val) -{ - u8 tmp[2] = { reg, val }; - struct spi_device *spi = lis3->bus_priv; - return spi_write(spi, tmp, sizeof(tmp)); -} - -static int lis3_spi_init(struct lis3lv02d *lis3) -{ - u8 reg; - int ret; - - /* power up the device */ - ret = lis3->read(lis3, CTRL_REG1, ®); - if (ret < 0) - return ret; - - reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; - return lis3->write(lis3, CTRL_REG1, reg); -} - -static union axis_conversion lis3lv02d_axis_normal = - { .as_array = { 1, 2, 3 } }; - -static int __devinit lis302dl_spi_probe(struct spi_device *spi) -{ - int ret; - - spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; - ret = spi_setup(spi); - if (ret < 0) - return ret; - - lis3_dev.bus_priv = spi; - lis3_dev.init = lis3_spi_init; - lis3_dev.read = lis3_spi_read; - lis3_dev.write = lis3_spi_write; - lis3_dev.irq = spi->irq; - lis3_dev.ac = lis3lv02d_axis_normal; - lis3_dev.pdata = spi->dev.platform_data; - spi_set_drvdata(spi, &lis3_dev); - - return lis3lv02d_init_device(&lis3_dev); -} - -static int __devexit lis302dl_spi_remove(struct spi_device *spi) -{ - struct lis3lv02d *lis3 = spi_get_drvdata(spi); - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(lis3); - - return lis3lv02d_remove_fs(&lis3_dev); -} - -#ifdef CONFIG_PM_SLEEP -static int lis3lv02d_spi_suspend(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct lis3lv02d *lis3 = spi_get_drvdata(spi); - - if (!lis3->pdata || !lis3->pdata->wakeup_flags) - lis3lv02d_poweroff(&lis3_dev); - - return 0; -} - -static int lis3lv02d_spi_resume(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct lis3lv02d *lis3 = spi_get_drvdata(spi); - - if (!lis3->pdata || !lis3->pdata->wakeup_flags) - lis3lv02d_poweron(lis3); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(lis3lv02d_spi_pm, lis3lv02d_spi_suspend, - lis3lv02d_spi_resume); - -static struct spi_driver lis302dl_spi_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .pm = &lis3lv02d_spi_pm, - }, - .probe = lis302dl_spi_probe, - .remove = __devexit_p(lis302dl_spi_remove), -}; - -static int __init lis302dl_init(void) -{ - return spi_register_driver(&lis302dl_spi_driver); -} - -static void __exit lis302dl_exit(void) -{ - spi_unregister_driver(&lis302dl_spi_driver); -} - -module_init(lis302dl_init); -module_exit(lis302dl_exit); - -MODULE_AUTHOR("Daniel Mack "); -MODULE_DESCRIPTION("lis3lv02d SPI glue layer"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b7d5ef234ac9..203500d9b848 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -2,6 +2,14 @@ # Misc strange devices # +# This one has to live outside of the MISC_DEVICES conditional, +# because it may be selected by drivers/platform/x86/hp_accel. +config SENSORS_LIS3LV02D + tristate + depends on INPUT + select INPUT_POLLDEV + default n + menuconfig MISC_DEVICES bool "Misc devices" ---help--- @@ -462,5 +470,6 @@ source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" source "drivers/misc/iwmc3200top/Kconfig" source "drivers/misc/ti-st/Kconfig" +source "drivers/misc/lis3lv02d/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 98009cc20cb9..804f421bc079 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o obj-y += ti-st/ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o +obj-y += lis3lv02d/ diff --git a/drivers/misc/lis3lv02d/Kconfig b/drivers/misc/lis3lv02d/Kconfig new file mode 100644 index 000000000000..8f474e6fc7b4 --- /dev/null +++ b/drivers/misc/lis3lv02d/Kconfig @@ -0,0 +1,37 @@ +# +# STMicroelectonics LIS3LV02D and similar accelerometers +# + +config SENSORS_LIS3_SPI + tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" + depends on !ACPI && SPI_MASTER && INPUT + select SENSORS_LIS3LV02D + default n + help + This driver provides support for the LIS3LV02Dx accelerometer connected + via SPI. The accelerometer data is readable via + /sys/devices/platform/lis3lv02d. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + This driver can also be built as modules. If so, the core module + will be called lis3lv02d and a specific module for the SPI transport + is called lis3lv02d_spi. + +config SENSORS_LIS3_I2C + tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)" + depends on I2C && INPUT + select SENSORS_LIS3LV02D + default n + help + This driver provides support for the LIS3LV02Dx accelerometer connected + via I2C. The accelerometer data is readable via + /sys/devices/platform/lis3lv02d. + + This driver also provides an absolute input class device, allowing + the device to act as a pinball machine-esque joystick. + + This driver can also be built as modules. If so, the core module + will be called lis3lv02d and a specific module for the I2C transport + is called lis3lv02d_i2c. diff --git a/drivers/misc/lis3lv02d/Makefile b/drivers/misc/lis3lv02d/Makefile new file mode 100644 index 000000000000..4bf58b16fcf8 --- /dev/null +++ b/drivers/misc/lis3lv02d/Makefile @@ -0,0 +1,7 @@ +# +# STMicroelectonics LIS3LV02D and similar accelerometers +# + +obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o +obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d_spi.o +obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d_i2c.o diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c new file mode 100644 index 000000000000..b928bc14e97b --- /dev/null +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -0,0 +1,999 @@ +/* + * lis3lv02d.c - ST LIS3LV02DL accelerometer driver + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008-2009 Pavel Machek + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lis3lv02d.h" + +#define DRIVER_NAME "lis3lv02d" + +/* joystick device poll interval in milliseconds */ +#define MDPS_POLL_INTERVAL 50 +#define MDPS_POLL_MIN 0 +#define MDPS_POLL_MAX 2000 + +#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ + +#define SELFTEST_OK 0 +#define SELFTEST_FAIL -1 +#define SELFTEST_IRQ -2 + +#define IRQ_LINE0 0 +#define IRQ_LINE1 1 + +/* + * The sensor can also generate interrupts (DRDY) but it's pretty pointless + * because they are generated even if the data do not change. So it's better + * to keep the interrupt for the free-fall event. The values are updated at + * 40Hz (at the lowest frequency), but as it can be pretty time consuming on + * some low processor, we poll the sensor only at 20Hz... enough for the + * joystick. + */ + +#define LIS3_PWRON_DELAY_WAI_12B (5000) +#define LIS3_PWRON_DELAY_WAI_8B (3000) + +/* + * LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG + * LIS302D spec says: 18 mG / digit + * LIS3_ACCURACY is used to increase accuracy of the intermediate + * calculation results. + */ +#define LIS3_ACCURACY 1024 +/* Sensitivity values for -2G +2G scale */ +#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024) +#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY) + +#define LIS3_DEFAULT_FUZZ_12B 3 +#define LIS3_DEFAULT_FLAT_12B 3 +#define LIS3_DEFAULT_FUZZ_8B 1 +#define LIS3_DEFAULT_FLAT_8B 1 + +struct lis3lv02d lis3_dev = { + .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), +}; +EXPORT_SYMBOL_GPL(lis3_dev); + +/* just like param_set_int() but does sanity-check so that it won't point + * over the axis array size + */ +static int param_set_axis(const char *val, const struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + if (!ret) { + int val = *(int *)kp->arg; + if (val < 0) + val = -val; + if (!val || val > 3) + return -EINVAL; + } + return ret; +} + +static struct kernel_param_ops param_ops_axis = { + .set = param_set_axis, + .get = param_get_int, +}; + +module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644); +MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions"); + +static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) +{ + s8 lo; + if (lis3->read(lis3, reg, &lo) < 0) + return 0; + + return lo; +} + +static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg) +{ + u8 lo, hi; + + lis3->read(lis3, reg - 1, &lo); + lis3->read(lis3, reg, &hi); + /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ + return (s16)((hi << 8) | lo); +} + +/** + * lis3lv02d_get_axis - For the given axis, give the value converted + * @axis: 1,2,3 - can also be negative + * @hw_values: raw values returned by the hardware + * + * Returns the converted value. + */ +static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) +{ + if (axis > 0) + return hw_values[axis - 1]; + else + return -hw_values[-axis - 1]; +} + +/** + * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer + * @lis3: pointer to the device struct + * @x: where to store the X axis value + * @y: where to store the Y axis value + * @z: where to store the Z axis value + * + * Note that 40Hz input device can eat up about 10% CPU at 800MHZ + */ +static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) +{ + int position[3]; + int i; + + if (lis3->blkread) { + if (lis3_dev.whoami == WAI_12B) { + u16 data[3]; + lis3->blkread(lis3, OUTX_L, 6, (u8 *)data); + for (i = 0; i < 3; i++) + position[i] = (s16)le16_to_cpu(data[i]); + } else { + u8 data[5]; + /* Data: x, dummy, y, dummy, z */ + lis3->blkread(lis3, OUTX, 5, data); + for (i = 0; i < 3; i++) + position[i] = (s8)data[i * 2]; + } + } else { + position[0] = lis3->read_data(lis3, OUTX); + position[1] = lis3->read_data(lis3, OUTY); + position[2] = lis3->read_data(lis3, OUTZ); + } + + for (i = 0; i < 3; i++) + position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; + + *x = lis3lv02d_get_axis(lis3->ac.x, position); + *y = lis3lv02d_get_axis(lis3->ac.y, position); + *z = lis3lv02d_get_axis(lis3->ac.z, position); +} + +/* conversion btw sampling rate and the register values */ +static int lis3_12_rates[4] = {40, 160, 640, 2560}; +static int lis3_8_rates[2] = {100, 400}; +static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000}; + +/* ODR is Output Data Rate */ +static int lis3lv02d_get_odr(void) +{ + u8 ctrl; + int shift; + + lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); + ctrl &= lis3_dev.odr_mask; + shift = ffs(lis3_dev.odr_mask) - 1; + return lis3_dev.odrs[(ctrl >> shift)]; +} + +static int lis3lv02d_set_odr(int rate) +{ + u8 ctrl; + int i, len, shift; + + if (!rate) + return -EINVAL; + + lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); + ctrl &= ~lis3_dev.odr_mask; + len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ + shift = ffs(lis3_dev.odr_mask) - 1; + + for (i = 0; i < len; i++) + if (lis3_dev.odrs[i] == rate) { + lis3_dev.write(&lis3_dev, CTRL_REG1, + ctrl | (i << shift)); + return 0; + } + return -EINVAL; +} + +static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) +{ + u8 ctlreg, reg; + s16 x, y, z; + u8 selftest; + int ret; + u8 ctrl_reg_data; + unsigned char irq_cfg; + + mutex_lock(&lis3->mutex); + + irq_cfg = lis3->irq_cfg; + if (lis3_dev.whoami == WAI_8B) { + lis3->data_ready_count[IRQ_LINE0] = 0; + lis3->data_ready_count[IRQ_LINE1] = 0; + + /* Change interrupt cfg to data ready for selftest */ + atomic_inc(&lis3_dev.wake_thread); + lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY; + lis3->read(lis3, CTRL_REG3, &ctrl_reg_data); + lis3->write(lis3, CTRL_REG3, (ctrl_reg_data & + ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) | + (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY)); + } + + if (lis3_dev.whoami == WAI_3DC) { + ctlreg = CTRL_REG4; + selftest = CTRL4_ST0; + } else { + ctlreg = CTRL_REG1; + if (lis3_dev.whoami == WAI_12B) + selftest = CTRL1_ST; + else + selftest = CTRL1_STP; + } + + lis3->read(lis3, ctlreg, ®); + lis3->write(lis3, ctlreg, (reg | selftest)); + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); + + /* Read directly to avoid axis remap */ + x = lis3->read_data(lis3, OUTX); + y = lis3->read_data(lis3, OUTY); + z = lis3->read_data(lis3, OUTZ); + + /* back to normal settings */ + lis3->write(lis3, ctlreg, reg); + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); + + results[0] = x - lis3->read_data(lis3, OUTX); + results[1] = y - lis3->read_data(lis3, OUTY); + results[2] = z - lis3->read_data(lis3, OUTZ); + + ret = 0; + + if (lis3_dev.whoami == WAI_8B) { + /* Restore original interrupt configuration */ + atomic_dec(&lis3_dev.wake_thread); + lis3->write(lis3, CTRL_REG3, ctrl_reg_data); + lis3->irq_cfg = irq_cfg; + + if ((irq_cfg & LIS3_IRQ1_MASK) && + lis3->data_ready_count[IRQ_LINE0] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + + if ((irq_cfg & LIS3_IRQ2_MASK) && + lis3->data_ready_count[IRQ_LINE1] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + } + + if (lis3->pdata) { + int i; + for (i = 0; i < 3; i++) { + /* Check against selftest acceptance limits */ + if ((results[i] < lis3->pdata->st_min_limits[i]) || + (results[i] > lis3->pdata->st_max_limits[i])) { + ret = SELFTEST_FAIL; + goto fail; + } + } + } + + /* test passed */ +fail: + mutex_unlock(&lis3->mutex); + return ret; +} + +/* + * Order of registers in the list affects to order of the restore process. + * Perhaps it is a good idea to set interrupt enable register as a last one + * after all other configurations + */ +static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, + FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, + CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, + CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, + CTRL_REG1, CTRL_REG2, CTRL_REG3}; + +static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, + FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, + DD_THSE_L, DD_THSE_H, + CTRL_REG1, CTRL_REG3, CTRL_REG2}; + +static inline void lis3_context_save(struct lis3lv02d *lis3) +{ + int i; + for (i = 0; i < lis3->regs_size; i++) + lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); + lis3->regs_stored = true; +} + +static inline void lis3_context_restore(struct lis3lv02d *lis3) +{ + int i; + if (lis3->regs_stored) + for (i = 0; i < lis3->regs_size; i++) + lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); +} + +void lis3lv02d_poweroff(struct lis3lv02d *lis3) +{ + if (lis3->reg_ctrl) + lis3_context_save(lis3); + /* disable X,Y,Z axis and power down */ + lis3->write(lis3, CTRL_REG1, 0x00); + if (lis3->reg_ctrl) + lis3->reg_ctrl(lis3, LIS3_REG_OFF); +} +EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); + +void lis3lv02d_poweron(struct lis3lv02d *lis3) +{ + u8 reg; + + lis3->init(lis3); + + /* + * Common configuration + * BDU: (12 bits sensors only) LSB and MSB values are not updated until + * both have been read. So the value read will always be correct. + * Set BOOT bit to refresh factory tuning values. + */ + lis3->read(lis3, CTRL_REG2, ®); + if (lis3->whoami == WAI_12B) + reg |= CTRL2_BDU | CTRL2_BOOT; + else + reg |= CTRL2_BOOT_8B; + lis3->write(lis3, CTRL_REG2, reg); + + /* LIS3 power on delay is quite long */ + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); + + if (lis3->reg_ctrl) + lis3_context_restore(lis3); +} +EXPORT_SYMBOL_GPL(lis3lv02d_poweron); + + +static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) +{ + int x, y, z; + + mutex_lock(&lis3_dev.mutex); + lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); + input_report_abs(pidev->input, ABS_X, x); + input_report_abs(pidev->input, ABS_Y, y); + input_report_abs(pidev->input, ABS_Z, z); + input_sync(pidev->input); + mutex_unlock(&lis3_dev.mutex); +} + +static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) +{ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + + if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev) + atomic_set(&lis3_dev.wake_thread, 1); + /* + * Update coordinates for the case where poll interval is 0 and + * the chip in running purely under interrupt control + */ + lis3lv02d_joystick_poll(pidev); +} + +static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) +{ + atomic_set(&lis3_dev.wake_thread, 0); + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); +} + +static irqreturn_t lis302dl_interrupt(int irq, void *dummy) +{ + if (!test_bit(0, &lis3_dev.misc_opened)) + goto out; + + /* + * Be careful: on some HP laptops the bios force DD when on battery and + * the lid is closed. This leads to interrupts as soon as a little move + * is done. + */ + atomic_inc(&lis3_dev.count); + + wake_up_interruptible(&lis3_dev.misc_wait); + kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); +out: + if (atomic_read(&lis3_dev.wake_thread)) + return IRQ_WAKE_THREAD; + return IRQ_HANDLED; +} + +static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) +{ + struct input_dev *dev = lis3->idev->input; + u8 click_src; + + mutex_lock(&lis3->mutex); + lis3->read(lis3, CLICK_SRC, &click_src); + + if (click_src & CLICK_SINGLE_X) { + input_report_key(dev, lis3->mapped_btns[0], 1); + input_report_key(dev, lis3->mapped_btns[0], 0); + } + + if (click_src & CLICK_SINGLE_Y) { + input_report_key(dev, lis3->mapped_btns[1], 1); + input_report_key(dev, lis3->mapped_btns[1], 0); + } + + if (click_src & CLICK_SINGLE_Z) { + input_report_key(dev, lis3->mapped_btns[2], 1); + input_report_key(dev, lis3->mapped_btns[2], 0); + } + input_sync(dev); + mutex_unlock(&lis3->mutex); +} + +static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index) +{ + int dummy; + + /* Dummy read to ack interrupt */ + lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy); + lis3->data_ready_count[index]++; +} + +static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) +{ + struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK; + + if (irq_cfg == LIS3_IRQ1_CLICK) + lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE0); + else + lis3lv02d_joystick_poll(lis3->idev); + + return IRQ_HANDLED; +} + +static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) +{ + struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK; + + if (irq_cfg == LIS3_IRQ2_CLICK) + lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE1); + else + lis3lv02d_joystick_poll(lis3->idev); + + return IRQ_HANDLED; +} + +static int lis3lv02d_misc_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &lis3_dev.misc_opened)) + return -EBUSY; /* already open */ + + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + + atomic_set(&lis3_dev.count, 0); + return 0; +} + +static int lis3lv02d_misc_release(struct inode *inode, struct file *file) +{ + fasync_helper(-1, file, 0, &lis3_dev.async_queue); + clear_bit(0, &lis3_dev.misc_opened); /* release the device */ + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); + return 0; +} + +static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + DECLARE_WAITQUEUE(wait, current); + u32 data; + unsigned char byte_data; + ssize_t retval = 1; + + if (count < 1) + return -EINVAL; + + add_wait_queue(&lis3_dev.misc_wait, &wait); + while (true) { + set_current_state(TASK_INTERRUPTIBLE); + data = atomic_xchg(&lis3_dev.count, 0); + if (data) + break; + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data < 255) + byte_data = data; + else + byte_data = 255; + + /* make sure we are not going into copy_to_user() with + * TASK_INTERRUPTIBLE state */ + set_current_state(TASK_RUNNING); + if (copy_to_user(buf, &byte_data, sizeof(byte_data))) + retval = -EFAULT; + +out: + __set_current_state(TASK_RUNNING); + remove_wait_queue(&lis3_dev.misc_wait, &wait); + + return retval; +} + +static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &lis3_dev.misc_wait, wait); + if (atomic_read(&lis3_dev.count)) + return POLLIN | POLLRDNORM; + return 0; +} + +static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) +{ + return fasync_helper(fd, file, on, &lis3_dev.async_queue); +} + +static const struct file_operations lis3lv02d_misc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = lis3lv02d_misc_read, + .open = lis3lv02d_misc_open, + .release = lis3lv02d_misc_release, + .poll = lis3lv02d_misc_poll, + .fasync = lis3lv02d_misc_fasync, +}; + +static struct miscdevice lis3lv02d_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "freefall", + .fops = &lis3lv02d_misc_fops, +}; + +int lis3lv02d_joystick_enable(void) +{ + struct input_dev *input_dev; + int err; + int max_val, fuzz, flat; + int btns[] = {BTN_X, BTN_Y, BTN_Z}; + + if (lis3_dev.idev) + return -EINVAL; + + lis3_dev.idev = input_allocate_polled_device(); + if (!lis3_dev.idev) + return -ENOMEM; + + lis3_dev.idev->poll = lis3lv02d_joystick_poll; + lis3_dev.idev->open = lis3lv02d_joystick_open; + lis3_dev.idev->close = lis3lv02d_joystick_close; + lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; + lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; + lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; + input_dev = lis3_dev.idev->input; + + input_dev->name = "ST LIS3LV02DL Accelerometer"; + input_dev->phys = DRIVER_NAME "/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0; + input_dev->dev.parent = &lis3_dev.pdev->dev; + + set_bit(EV_ABS, input_dev->evbit); + max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY; + if (lis3_dev.whoami == WAI_12B) { + fuzz = LIS3_DEFAULT_FUZZ_12B; + flat = LIS3_DEFAULT_FLAT_12B; + } else { + fuzz = LIS3_DEFAULT_FUZZ_8B; + flat = LIS3_DEFAULT_FLAT_8B; + } + fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY; + flat = (flat * lis3_dev.scale) / LIS3_ACCURACY; + + input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat); + input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); + input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); + + lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns); + lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns); + lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns); + + err = input_register_polled_device(lis3_dev.idev); + if (err) { + input_free_polled_device(lis3_dev.idev); + lis3_dev.idev = NULL; + } + + return err; +} +EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); + +void lis3lv02d_joystick_disable(void) +{ + if (lis3_dev.irq) + free_irq(lis3_dev.irq, &lis3_dev); + if (lis3_dev.pdata && lis3_dev.pdata->irq2) + free_irq(lis3_dev.pdata->irq2, &lis3_dev); + + if (!lis3_dev.idev) + return; + + if (lis3_dev.irq) + misc_deregister(&lis3lv02d_misc_device); + input_unregister_polled_device(lis3_dev.idev); + input_free_polled_device(lis3_dev.idev); + lis3_dev.idev = NULL; +} +EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); + +/* Sysfs stuff */ +static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) +{ + /* + * SYSFS functions are fast visitors so put-call + * immediately after the get-call. However, keep + * chip running for a while and schedule delayed + * suspend. This way periodic sysfs calls doesn't + * suffer from relatively long power up time. + */ + + if (lis3->pm_dev) { + pm_runtime_get_sync(lis3->pm_dev); + pm_runtime_put_noidle(lis3->pm_dev); + pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); + } +} + +static ssize_t lis3lv02d_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s16 values[3]; + + static const char ok[] = "OK"; + static const char fail[] = "FAIL"; + static const char irq[] = "FAIL_IRQ"; + const char *res; + + lis3lv02d_sysfs_poweron(&lis3_dev); + switch (lis3lv02d_selftest(&lis3_dev, values)) { + case SELFTEST_FAIL: + res = fail; + break; + case SELFTEST_IRQ: + res = irq; + break; + case SELFTEST_OK: + default: + res = ok; + break; + } + return sprintf(buf, "%s %d %d %d\n", res, + values[0], values[1], values[2]); +} + +static ssize_t lis3lv02d_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int x, y, z; + + lis3lv02d_sysfs_poweron(&lis3_dev); + mutex_lock(&lis3_dev.mutex); + lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); + mutex_unlock(&lis3_dev.mutex); + return sprintf(buf, "(%d,%d,%d)\n", x, y, z); +} + +static ssize_t lis3lv02d_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + lis3lv02d_sysfs_poweron(&lis3_dev); + return sprintf(buf, "%d\n", lis3lv02d_get_odr()); +} + +static ssize_t lis3lv02d_rate_set(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned long rate; + + if (strict_strtoul(buf, 0, &rate)) + return -EINVAL; + + lis3lv02d_sysfs_poweron(&lis3_dev); + if (lis3lv02d_set_odr(rate)) + return -EINVAL; + + return count; +} + +static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL); +static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); +static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show, + lis3lv02d_rate_set); + +static struct attribute *lis3lv02d_attributes[] = { + &dev_attr_selftest.attr, + &dev_attr_position.attr, + &dev_attr_rate.attr, + NULL +}; + +static struct attribute_group lis3lv02d_attribute_group = { + .attrs = lis3lv02d_attributes +}; + + +static int lis3lv02d_add_fs(struct lis3lv02d *lis3) +{ + lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(lis3->pdev)) + return PTR_ERR(lis3->pdev); + + return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); +} + +int lis3lv02d_remove_fs(struct lis3lv02d *lis3) +{ + sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); + platform_device_unregister(lis3->pdev); + if (lis3->pm_dev) { + /* Barrier after the sysfs remove */ + pm_runtime_barrier(lis3->pm_dev); + + /* SYSFS may have left chip running. Turn off if necessary */ + if (!pm_runtime_suspended(lis3->pm_dev)) + lis3lv02d_poweroff(&lis3_dev); + + pm_runtime_disable(lis3->pm_dev); + pm_runtime_set_suspended(lis3->pm_dev); + } + kfree(lis3->reg_cache); + return 0; +} +EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); + +static void lis3lv02d_8b_configure(struct lis3lv02d *dev, + struct lis3lv02d_platform_data *p) +{ + int err; + int ctrl2 = p->hipass_ctrl; + + if (p->click_flags) { + dev->write(dev, CLICK_CFG, p->click_flags); + dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit); + dev->write(dev, CLICK_LATENCY, p->click_latency); + dev->write(dev, CLICK_WINDOW, p->click_window); + dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf); + dev->write(dev, CLICK_THSY_X, + (p->click_thresh_x & 0xf) | + (p->click_thresh_y << 4)); + + if (dev->idev) { + struct input_dev *input_dev = lis3_dev.idev->input; + input_set_capability(input_dev, EV_KEY, BTN_X); + input_set_capability(input_dev, EV_KEY, BTN_Y); + input_set_capability(input_dev, EV_KEY, BTN_Z); + } + } + + if (p->wakeup_flags) { + dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); + dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1); + ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/ + } + + if (p->wakeup_flags2) { + dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2); + dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1); + ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/ + } + /* Configure hipass filters */ + dev->write(dev, CTRL_REG2, ctrl2); + + if (p->irq2) { + err = request_threaded_irq(p->irq2, + NULL, + lis302dl_interrupt_thread2_8b, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + (p->irq_flags2 & IRQF_TRIGGER_MASK), + DRIVER_NAME, &lis3_dev); + if (err < 0) + pr_err("No second IRQ. Limited functionality\n"); + } +} + +/* + * Initialise the accelerometer and the various subsystems. + * Should be rather independent of the bus system. + */ +int lis3lv02d_init_device(struct lis3lv02d *dev) +{ + int err; + irq_handler_t thread_fn; + int irq_flags = 0; + + dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); + + switch (dev->whoami) { + case WAI_12B: + pr_info("12 bits sensor found\n"); + dev->read_data = lis3lv02d_read_12; + dev->mdps_max_val = 2048; + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B; + dev->odrs = lis3_12_rates; + dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; + dev->scale = LIS3_SENSITIVITY_12B; + dev->regs = lis3_wai12_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); + break; + case WAI_8B: + pr_info("8 bits sensor found\n"); + dev->read_data = lis3lv02d_read_8; + dev->mdps_max_val = 128; + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; + dev->odrs = lis3_8_rates; + dev->odr_mask = CTRL1_DR; + dev->scale = LIS3_SENSITIVITY_8B; + dev->regs = lis3_wai8_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); + break; + case WAI_3DC: + pr_info("8 bits 3DC sensor found\n"); + dev->read_data = lis3lv02d_read_8; + dev->mdps_max_val = 128; + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; + dev->odrs = lis3_3dc_rates; + dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3; + dev->scale = LIS3_SENSITIVITY_8B; + break; + default: + pr_err("unknown sensor type 0x%X\n", dev->whoami); + return -EINVAL; + } + + dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), + sizeof(lis3_wai12_regs)), GFP_KERNEL); + + if (dev->reg_cache == NULL) { + printk(KERN_ERR DRIVER_NAME "out of memory\n"); + return -ENOMEM; + } + + mutex_init(&dev->mutex); + atomic_set(&dev->wake_thread, 0); + + lis3lv02d_add_fs(dev); + lis3lv02d_poweron(dev); + + if (dev->pm_dev) { + pm_runtime_set_active(dev->pm_dev); + pm_runtime_enable(dev->pm_dev); + } + + if (lis3lv02d_joystick_enable()) + pr_err("joystick initialization failed\n"); + + /* passing in platform specific data is purely optional and only + * used by the SPI transport layer at the moment */ + if (dev->pdata) { + struct lis3lv02d_platform_data *p = dev->pdata; + + if (dev->whoami == WAI_8B) + lis3lv02d_8b_configure(dev, p); + + irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK; + + dev->irq_cfg = p->irq_cfg; + if (p->irq_cfg) + dev->write(dev, CTRL_REG3, p->irq_cfg); + + if (p->default_rate) + lis3lv02d_set_odr(p->default_rate); + } + + /* bail if we did not get an IRQ from the bus layer */ + if (!dev->irq) { + pr_debug("No IRQ. Disabling /dev/freefall\n"); + goto out; + } + + /* + * The sensor can generate interrupts for free-fall and direction + * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep + * the things simple and _fast_ we activate it only for free-fall, so + * no need to read register (very slow with ACPI). For the same reason, + * we forbid shared interrupts. + * + * IRQF_TRIGGER_RISING seems pointless on HP laptops because the + * io-apic is not configurable (and generates a warning) but I keep it + * in case of support for other hardware. + */ + if (dev->pdata && dev->whoami == WAI_8B) + thread_fn = lis302dl_interrupt_thread1_8b; + else + thread_fn = NULL; + + err = request_threaded_irq(dev->irq, lis302dl_interrupt, + thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + irq_flags, + DRIVER_NAME, &lis3_dev); + + if (err < 0) { + pr_err("Cannot get IRQ\n"); + goto out; + } + + if (misc_register(&lis3lv02d_misc_device)) + pr_err("misc_register failed\n"); +out: + return 0; +} +EXPORT_SYMBOL_GPL(lis3lv02d_init_device); + +MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h new file mode 100644 index 000000000000..a1939589eb2c --- /dev/null +++ b/drivers/misc/lis3lv02d/lis3lv02d.h @@ -0,0 +1,291 @@ +/* + * lis3lv02d.h - ST LIS3LV02DL accelerometer driver + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008-2009 Eric Piel + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include + +/* + * This driver tries to support the "digital" accelerometer chips from + * STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL, + * LIS35DE, or LIS202DL. They are very similar in terms of programming, with + * almost the same registers. In addition to differing on physical properties, + * they differ on the number of axes (2/3), precision (8/12 bits), and special + * features (freefall detection, click...). Unfortunately, not all the + * differences can be probed via a register. + * They can be connected either via I²C or SPI. + */ + +#include + +enum lis3_reg { + WHO_AM_I = 0x0F, + OFFSET_X = 0x16, + OFFSET_Y = 0x17, + OFFSET_Z = 0x18, + GAIN_X = 0x19, + GAIN_Y = 0x1A, + GAIN_Z = 0x1B, + CTRL_REG1 = 0x20, + CTRL_REG2 = 0x21, + CTRL_REG3 = 0x22, + CTRL_REG4 = 0x23, + HP_FILTER_RESET = 0x23, + STATUS_REG = 0x27, + OUTX_L = 0x28, + OUTX_H = 0x29, + OUTX = 0x29, + OUTY_L = 0x2A, + OUTY_H = 0x2B, + OUTY = 0x2B, + OUTZ_L = 0x2C, + OUTZ_H = 0x2D, + OUTZ = 0x2D, +}; + +enum lis302d_reg { + FF_WU_CFG_1 = 0x30, + FF_WU_SRC_1 = 0x31, + FF_WU_THS_1 = 0x32, + FF_WU_DURATION_1 = 0x33, + FF_WU_CFG_2 = 0x34, + FF_WU_SRC_2 = 0x35, + FF_WU_THS_2 = 0x36, + FF_WU_DURATION_2 = 0x37, + CLICK_CFG = 0x38, + CLICK_SRC = 0x39, + CLICK_THSY_X = 0x3B, + CLICK_THSZ = 0x3C, + CLICK_TIMELIMIT = 0x3D, + CLICK_LATENCY = 0x3E, + CLICK_WINDOW = 0x3F, +}; + +enum lis3lv02d_reg { + FF_WU_CFG = 0x30, + FF_WU_SRC = 0x31, + FF_WU_ACK = 0x32, + FF_WU_THS_L = 0x34, + FF_WU_THS_H = 0x35, + FF_WU_DURATION = 0x36, + DD_CFG = 0x38, + DD_SRC = 0x39, + DD_ACK = 0x3A, + DD_THSI_L = 0x3C, + DD_THSI_H = 0x3D, + DD_THSE_L = 0x3E, + DD_THSE_H = 0x3F, +}; + +enum lis3_who_am_i { + WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */ + WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ + WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ + WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ +}; + +enum lis3lv02d_ctrl1_12b { + CTRL1_Xen = 0x01, + CTRL1_Yen = 0x02, + CTRL1_Zen = 0x04, + CTRL1_ST = 0x08, + CTRL1_DF0 = 0x10, + CTRL1_DF1 = 0x20, + CTRL1_PD0 = 0x40, + CTRL1_PD1 = 0x80, +}; + +/* Delta to ctrl1_12b version */ +enum lis3lv02d_ctrl1_8b { + CTRL1_STM = 0x08, + CTRL1_STP = 0x10, + CTRL1_FS = 0x20, + CTRL1_PD = 0x40, + CTRL1_DR = 0x80, +}; + +enum lis3lv02d_ctrl1_3dc { + CTRL1_ODR0 = 0x10, + CTRL1_ODR1 = 0x20, + CTRL1_ODR2 = 0x40, + CTRL1_ODR3 = 0x80, +}; + +enum lis3lv02d_ctrl2 { + CTRL2_DAS = 0x01, + CTRL2_SIM = 0x02, + CTRL2_DRDY = 0x04, + CTRL2_IEN = 0x08, + CTRL2_BOOT = 0x10, + CTRL2_BLE = 0x20, + CTRL2_BDU = 0x40, /* Block Data Update */ + CTRL2_FS = 0x80, /* Full Scale selection */ +}; + +enum lis3lv02d_ctrl4_3dc { + CTRL4_SIM = 0x01, + CTRL4_ST0 = 0x02, + CTRL4_ST1 = 0x04, + CTRL4_FS0 = 0x10, + CTRL4_FS1 = 0x20, +}; + +enum lis302d_ctrl2 { + HP_FF_WU2 = 0x08, + HP_FF_WU1 = 0x04, + CTRL2_BOOT_8B = 0x40, +}; + +enum lis3lv02d_ctrl3 { + CTRL3_CFS0 = 0x01, + CTRL3_CFS1 = 0x02, + CTRL3_FDS = 0x10, + CTRL3_HPFF = 0x20, + CTRL3_HPDD = 0x40, + CTRL3_ECK = 0x80, +}; + +enum lis3lv02d_status_reg { + STATUS_XDA = 0x01, + STATUS_YDA = 0x02, + STATUS_ZDA = 0x04, + STATUS_XYZDA = 0x08, + STATUS_XOR = 0x10, + STATUS_YOR = 0x20, + STATUS_ZOR = 0x40, + STATUS_XYZOR = 0x80, +}; + +enum lis3lv02d_ff_wu_cfg { + FF_WU_CFG_XLIE = 0x01, + FF_WU_CFG_XHIE = 0x02, + FF_WU_CFG_YLIE = 0x04, + FF_WU_CFG_YHIE = 0x08, + FF_WU_CFG_ZLIE = 0x10, + FF_WU_CFG_ZHIE = 0x20, + FF_WU_CFG_LIR = 0x40, + FF_WU_CFG_AOI = 0x80, +}; + +enum lis3lv02d_ff_wu_src { + FF_WU_SRC_XL = 0x01, + FF_WU_SRC_XH = 0x02, + FF_WU_SRC_YL = 0x04, + FF_WU_SRC_YH = 0x08, + FF_WU_SRC_ZL = 0x10, + FF_WU_SRC_ZH = 0x20, + FF_WU_SRC_IA = 0x40, +}; + +enum lis3lv02d_dd_cfg { + DD_CFG_XLIE = 0x01, + DD_CFG_XHIE = 0x02, + DD_CFG_YLIE = 0x04, + DD_CFG_YHIE = 0x08, + DD_CFG_ZLIE = 0x10, + DD_CFG_ZHIE = 0x20, + DD_CFG_LIR = 0x40, + DD_CFG_IEND = 0x80, +}; + +enum lis3lv02d_dd_src { + DD_SRC_XL = 0x01, + DD_SRC_XH = 0x02, + DD_SRC_YL = 0x04, + DD_SRC_YH = 0x08, + DD_SRC_ZL = 0x10, + DD_SRC_ZH = 0x20, + DD_SRC_IA = 0x40, +}; + +enum lis3lv02d_click_src_8b { + CLICK_SINGLE_X = 0x01, + CLICK_DOUBLE_X = 0x02, + CLICK_SINGLE_Y = 0x04, + CLICK_DOUBLE_Y = 0x08, + CLICK_SINGLE_Z = 0x10, + CLICK_DOUBLE_Z = 0x20, + CLICK_IA = 0x40, +}; + +enum lis3lv02d_reg_state { + LIS3_REG_OFF = 0x00, + LIS3_REG_ON = 0x01, +}; + +union axis_conversion { + struct { + int x, y, z; + }; + int as_array[3]; + +}; + +struct lis3lv02d { + void *bus_priv; /* used by the bus layer only */ + struct device *pm_dev; /* for pm_runtime purposes */ + int (*init) (struct lis3lv02d *lis3); + int (*write) (struct lis3lv02d *lis3, int reg, u8 val); + int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); + int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret); + int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); + + int *odrs; /* Supported output data rates */ + u8 *regs; /* Regs to store / restore */ + int regs_size; + u8 *reg_cache; + bool regs_stored; + u8 odr_mask; /* ODR bit mask */ + u8 whoami; /* indicates measurement precision */ + s16 (*read_data) (struct lis3lv02d *lis3, int reg); + int mdps_max_val; + int pwron_delay; + int scale; /* + * relationship between 1 LBS and mG + * (1/1000th of earth gravity) + */ + + struct input_polled_dev *idev; /* input device */ + struct platform_device *pdev; /* platform device */ + struct regulator_bulk_data regulators[2]; + atomic_t count; /* interrupt count after last read */ + union axis_conversion ac; /* hw -> logical axis */ + int mapped_btns[3]; + + u32 irq; /* IRQ number */ + struct fasync_struct *async_queue; /* queue for the misc device */ + wait_queue_head_t misc_wait; /* Wait queue for the misc device */ + unsigned long misc_opened; /* bit0: whether the device is open */ + int data_ready_count[2]; + atomic_t wake_thread; + unsigned char irq_cfg; + + struct lis3lv02d_platform_data *pdata; /* for passing board config */ + struct mutex mutex; /* Serialize poll and selftest */ +}; + +int lis3lv02d_init_device(struct lis3lv02d *lis3); +int lis3lv02d_joystick_enable(void); +void lis3lv02d_joystick_disable(void); +void lis3lv02d_poweroff(struct lis3lv02d *lis3); +void lis3lv02d_poweron(struct lis3lv02d *lis3); +int lis3lv02d_remove_fs(struct lis3lv02d *lis3); + +extern struct lis3lv02d lis3_dev; diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c new file mode 100644 index 000000000000..b20dfb4522d2 --- /dev/null +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -0,0 +1,279 @@ +/* + * drivers/hwmon/lis3lv02d_i2c.c + * + * Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer. + * Driver is based on corresponding SPI driver written by Daniel Mack + * (lis3lv02d_spi.c (C) 2009 Daniel Mack ). + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: Samu Onkalo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "lis3lv02d.h" + +#define DRV_NAME "lis3lv02d_i2c" + +static const char reg_vdd[] = "Vdd"; +static const char reg_vdd_io[] = "Vdd_IO"; + +static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) +{ + int ret; + if (state == LIS3_REG_OFF) { + ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + } else { + ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + /* Chip needs time to wakeup. Not mentioned in datasheet */ + usleep_range(10000, 20000); + } + return ret; +} + +static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) +{ + struct i2c_client *c = lis3->bus_priv; + return i2c_smbus_write_byte_data(c, reg, value); +} + +static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) +{ + struct i2c_client *c = lis3->bus_priv; + *v = i2c_smbus_read_byte_data(c, reg); + return 0; +} + +static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, + u8 *v) +{ + struct i2c_client *c = lis3->bus_priv; + reg |= (1 << 7); /* 7th bit enables address auto incrementation */ + return i2c_smbus_read_i2c_block_data(c, reg, len, v); +} + +static int lis3_i2c_init(struct lis3lv02d *lis3) +{ + u8 reg; + int ret; + + if (lis3->reg_ctrl) + lis3_reg_ctrl(lis3, LIS3_REG_ON); + + lis3->read(lis3, WHO_AM_I, ®); + if (reg != lis3->whoami) + printk(KERN_ERR "lis3: power on failure\n"); + + /* power up the device */ + ret = lis3->read(lis3, CTRL_REG1, ®); + if (ret < 0) + return ret; + + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; + return lis3->write(lis3, CTRL_REG1, reg); +} + +/* Default axis mapping but it can be overwritten by platform data */ +static union axis_conversion lis3lv02d_axis_map = + { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; + +static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct lis3lv02d_platform_data *pdata = client->dev.platform_data; + + if (pdata) { + /* Regulator control is optional */ + if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) + lis3_dev.reg_ctrl = lis3_reg_ctrl; + + if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK))) + lis3_dev.blkread = lis3_i2c_blockread; + + if (pdata->axis_x) + lis3lv02d_axis_map.x = pdata->axis_x; + + if (pdata->axis_y) + lis3lv02d_axis_map.y = pdata->axis_y; + + if (pdata->axis_z) + lis3lv02d_axis_map.z = pdata->axis_z; + + if (pdata->setup_resources) + ret = pdata->setup_resources(); + + if (ret) + goto fail; + } + + if (lis3_dev.reg_ctrl) { + lis3_dev.regulators[0].supply = reg_vdd; + lis3_dev.regulators[1].supply = reg_vdd_io; + ret = regulator_bulk_get(&client->dev, + ARRAY_SIZE(lis3_dev.regulators), + lis3_dev.regulators); + if (ret < 0) + goto fail; + } + + lis3_dev.pdata = pdata; + lis3_dev.bus_priv = client; + lis3_dev.init = lis3_i2c_init; + lis3_dev.read = lis3_i2c_read; + lis3_dev.write = lis3_i2c_write; + lis3_dev.irq = client->irq; + lis3_dev.ac = lis3lv02d_axis_map; + lis3_dev.pm_dev = &client->dev; + + i2c_set_clientdata(client, &lis3_dev); + + /* Provide power over the init call */ + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); + + ret = lis3lv02d_init_device(&lis3_dev); + + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); + + if (ret == 0) + return 0; +fail: + if (pdata && pdata->release_resources) + pdata->release_resources(); + return ret; +} + +static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) +{ + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + struct lis3lv02d_platform_data *pdata = client->dev.platform_data; + + if (pdata && pdata->release_resources) + pdata->release_resources(); + + lis3lv02d_joystick_disable(); + lis3lv02d_remove_fs(&lis3_dev); + + if (lis3_dev.reg_ctrl) + regulator_bulk_free(ARRAY_SIZE(lis3->regulators), + lis3_dev.regulators); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int lis3lv02d_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + if (!lis3->pdata || !lis3->pdata->wakeup_flags) + lis3lv02d_poweroff(lis3); + return 0; +} + +static int lis3lv02d_i2c_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + /* + * pm_runtime documentation says that devices should always + * be powered on at resume. Pm_runtime turns them off after system + * wide resume is complete. + */ + if (!lis3->pdata || !lis3->pdata->wakeup_flags || + pm_runtime_suspended(dev)) + lis3lv02d_poweron(lis3); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int lis3_i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweroff(lis3); + return 0; +} + +static int lis3_i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweron(lis3); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +static const struct i2c_device_id lis3lv02d_id[] = { + {"lis3lv02d", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); + +static const struct dev_pm_ops lis3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, + lis3lv02d_i2c_resume) + SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, + lis3_i2c_runtime_resume, + NULL) +}; + +static struct i2c_driver lis3lv02d_i2c_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &lis3_pm_ops, + }, + .probe = lis3lv02d_i2c_probe, + .remove = __devexit_p(lis3lv02d_i2c_remove), + .id_table = lis3lv02d_id, +}; + +static int __init lis3lv02d_init(void) +{ + return i2c_add_driver(&lis3lv02d_i2c_driver); +} + +static void __exit lis3lv02d_exit(void) +{ + i2c_del_driver(&lis3lv02d_i2c_driver); +} + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("lis3lv02d I2C interface"); +MODULE_LICENSE("GPL"); + +module_init(lis3lv02d_init); +module_exit(lis3lv02d_exit); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c new file mode 100644 index 000000000000..c1f8a8fbf694 --- /dev/null +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -0,0 +1,145 @@ +/* + * lis3lv02d_spi - SPI glue layer for lis3lv02d + * + * Copyright (c) 2009 Daniel Mack + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lis3lv02d.h" + +#define DRV_NAME "lis3lv02d_spi" +#define LIS3_SPI_READ 0x80 + +static int lis3_spi_read(struct lis3lv02d *lis3, int reg, u8 *v) +{ + struct spi_device *spi = lis3->bus_priv; + int ret = spi_w8r8(spi, reg | LIS3_SPI_READ); + if (ret < 0) + return -EINVAL; + + *v = (u8) ret; + return 0; +} + +static int lis3_spi_write(struct lis3lv02d *lis3, int reg, u8 val) +{ + u8 tmp[2] = { reg, val }; + struct spi_device *spi = lis3->bus_priv; + return spi_write(spi, tmp, sizeof(tmp)); +} + +static int lis3_spi_init(struct lis3lv02d *lis3) +{ + u8 reg; + int ret; + + /* power up the device */ + ret = lis3->read(lis3, CTRL_REG1, ®); + if (ret < 0) + return ret; + + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; + return lis3->write(lis3, CTRL_REG1, reg); +} + +static union axis_conversion lis3lv02d_axis_normal = + { .as_array = { 1, 2, 3 } }; + +static int __devinit lis302dl_spi_probe(struct spi_device *spi) +{ + int ret; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + lis3_dev.bus_priv = spi; + lis3_dev.init = lis3_spi_init; + lis3_dev.read = lis3_spi_read; + lis3_dev.write = lis3_spi_write; + lis3_dev.irq = spi->irq; + lis3_dev.ac = lis3lv02d_axis_normal; + lis3_dev.pdata = spi->dev.platform_data; + spi_set_drvdata(spi, &lis3_dev); + + return lis3lv02d_init_device(&lis3_dev); +} + +static int __devexit lis302dl_spi_remove(struct spi_device *spi) +{ + struct lis3lv02d *lis3 = spi_get_drvdata(spi); + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(lis3); + + return lis3lv02d_remove_fs(&lis3_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int lis3lv02d_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct lis3lv02d *lis3 = spi_get_drvdata(spi); + + if (!lis3->pdata || !lis3->pdata->wakeup_flags) + lis3lv02d_poweroff(&lis3_dev); + + return 0; +} + +static int lis3lv02d_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct lis3lv02d *lis3 = spi_get_drvdata(spi); + + if (!lis3->pdata || !lis3->pdata->wakeup_flags) + lis3lv02d_poweron(lis3); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(lis3lv02d_spi_pm, lis3lv02d_spi_suspend, + lis3lv02d_spi_resume); + +static struct spi_driver lis302dl_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &lis3lv02d_spi_pm, + }, + .probe = lis302dl_spi_probe, + .remove = __devexit_p(lis302dl_spi_remove), +}; + +static int __init lis302dl_init(void) +{ + return spi_register_driver(&lis302dl_spi_driver); +} + +static void __exit lis302dl_exit(void) +{ + spi_unregister_driver(&lis302dl_spi_driver); +} + +module_init(lis302dl_init); +module_exit(lis302dl_exit); + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("lis3lv02d SPI glue layer"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 23f09c1b234b..0a52711a6362 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -37,7 +37,7 @@ #include #include #include -#include "../../hwmon/lis3lv02d.h" +#include "../../misc/lis3lv02d/lis3lv02d.h" #define DRIVER_NAME "lis3lv02d" #define ACPI_MDPS_CLASS "accelerometer" -- cgit v1.2.3 From 93d0cc588345a9de304285781e010e298f74e06e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Mar 2011 17:59:36 +0100 Subject: hwmon: (abituguru*) Update my email address Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- MAINTAINERS | 2 +- drivers/hwmon/abituguru.c | 4 ++-- drivers/hwmon/abituguru3.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4edb2a800b54..666287a6f3da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -198,7 +198,7 @@ F: Documentation/scsi/aacraid.txt F: drivers/scsi/aacraid/ ABIT UGURU 1,2 HARDWARE MONITOR DRIVER -M: Hans de Goede +M: Hans de Goede L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/abituguru.c diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 8f07a9dda152..0e05aa179eaa 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1,5 +1,5 @@ /* - abituguru.c Copyright (c) 2005-2006 Hans de Goede + abituguru.c Copyright (c) 2005-2006 Hans de Goede 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 @@ -1505,7 +1505,7 @@ static void __exit abituguru_exit(void) platform_driver_unregister(&abituguru_driver); } -MODULE_AUTHOR("Hans de Goede "); +MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Abit uGuru Sensor device"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 48d21e22e930..034cebfcd273 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -1,7 +1,7 @@ /* abituguru3.c - Copyright (c) 2006-2008 Hans de Goede + Copyright (c) 2006-2008 Hans de Goede Copyright (c) 2008 Alistair John Strachan This program is free software; you can redistribute it and/or modify @@ -1266,7 +1266,7 @@ static void __exit abituguru3_exit(void) platform_driver_unregister(&abituguru3_driver); } -MODULE_AUTHOR("Hans de Goede "); +MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Abit uGuru3 Sensor device"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From a98d506c08ffe754fa013c7f70c4d578b991fb4b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Mar 2011 17:59:36 +0100 Subject: hwmon: New driver for SMSC SCH5627 SMSC SCH5627 Super I/O chips include complete hardware monitoring capabilities. They can monitor up to 5 voltages, 4 fans and 8 temperatures. The hardware monitoring part of the SMSC SCH5627 is accessed by talking through an embedded microcontroller. An application note describing the protocol for communicating with the microcontroller is available upon request. Please mail me if you want a copy. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/sch5627 | 22 ++ MAINTAINERS | 7 + drivers/hwmon/Kconfig | 9 + drivers/hwmon/Makefile | 1 + drivers/hwmon/sch5627.c | 858 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 897 insertions(+) create mode 100644 Documentation/hwmon/sch5627 create mode 100644 drivers/hwmon/sch5627.c (limited to 'MAINTAINERS') diff --git a/Documentation/hwmon/sch5627 b/Documentation/hwmon/sch5627 new file mode 100644 index 000000000000..446a054e4912 --- /dev/null +++ b/Documentation/hwmon/sch5627 @@ -0,0 +1,22 @@ +Kernel driver sch5627 +===================== + +Supported chips: + * SMSC SCH5627 + Prefix: 'sch5627' + Addresses scanned: none, address read from Super I/O config space + Datasheet: Application Note available upon request + +Author: Hans de Goede + + +Description +----------- + +SMSC SCH5627 Super I/O chips include complete hardware monitoring +capabilities. They can monitor up to 5 voltages, 4 fans and 8 temperatures. + +The hardware monitoring part of the SMSC SCH5627 is accessed by talking +through an embedded microcontroller. An application note describing the +protocol for communicating with the microcontroller is available upon +request. Please mail me if you want a copy. diff --git a/MAINTAINERS b/MAINTAINERS index 666287a6f3da..b8a123f7e810 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5769,6 +5769,13 @@ S: Supported F: Documentation/hwmon/emc2103 F: drivers/hwmon/emc2103.c +SMSC SCH5627 HARDWARE MONITOR DRIVER +M: Hans de Goede +L: lm-sensors@lm-sensors.org +S: Supported +F: Documentation/hwmon/sch5627 +F: drivers/hwmon/sch5627.c + SMSC47B397 HARDWARE MONITOR DRIVER M: "Mark M. Hoffman" L: lm-sensors@lm-sensors.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 19d72bcf966a..6fad9f082f67 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -959,6 +959,15 @@ config SENSORS_SMSC47B397 This driver can also be built as a module. If so, the module will be called smsc47b397. +config SENSORS_SCH5627 + tristate "SMSC SCH5627" + help + If you say yes here you get support for the hardware monitoring + features of the SMSC SCH5627 Super-I/O chip. + + This driver can also be built as a module. If so, the module + will be called sch5627. + config SENSORS_ADS7828 tristate "Texas Instruments ADS7828" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c068f82082cd..4d0122c52219 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o +obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c new file mode 100644 index 000000000000..9a51dcca9b0d --- /dev/null +++ b/drivers/hwmon/sch5627.c @@ -0,0 +1,858 @@ +/*************************************************************************** + * Copyright (C) 2010-2011 Hans de Goede * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "sch5627" +#define DEVNAME DRVNAME /* We only support one model */ + +#define SIO_SCH5627_EM_LD 0x0C /* Embedded Microcontroller LD */ +#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ + +#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ + +#define REGION_LENGTH 9 + +#define SCH5627_HWMON_ID 0xa5 +#define SCH5627_COMPANY_ID 0x5c +#define SCH5627_PRIMARY_ID 0xa0 + +#define SCH5627_REG_BUILD_CODE 0x39 +#define SCH5627_REG_BUILD_ID 0x3a +#define SCH5627_REG_HWMON_ID 0x3c +#define SCH5627_REG_HWMON_REV 0x3d +#define SCH5627_REG_COMPANY_ID 0x3e +#define SCH5627_REG_PRIMARY_ID 0x3f +#define SCH5627_REG_CTRL 0x40 + +#define SCH5627_NO_TEMPS 8 +#define SCH5627_NO_FANS 4 +#define SCH5627_NO_IN 5 + +static const u16 SCH5627_REG_TEMP_MSB[SCH5627_NO_TEMPS] = { + 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181 }; +static const u16 SCH5627_REG_TEMP_LSN[SCH5627_NO_TEMPS] = { + 0xE2, 0xE1, 0xE1, 0xE5, 0xE5, 0xE6, 0x182, 0x182 }; +static const u16 SCH5627_REG_TEMP_HIGH_NIBBLE[SCH5627_NO_TEMPS] = { + 0, 0, 1, 1, 0, 0, 0, 1 }; +static const u16 SCH5627_REG_TEMP_HIGH[SCH5627_NO_TEMPS] = { + 0x61, 0x57, 0x59, 0x5B, 0x5D, 0x5F, 0x184, 0x186 }; +static const u16 SCH5627_REG_TEMP_ABS[SCH5627_NO_TEMPS] = { + 0x9B, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x1A8, 0x1A9 }; + +static const u16 SCH5627_REG_FAN[SCH5627_NO_FANS] = { + 0x2C, 0x2E, 0x30, 0x32 }; +static const u16 SCH5627_REG_FAN_MIN[SCH5627_NO_FANS] = { + 0x62, 0x64, 0x66, 0x68 }; + +static const u16 SCH5627_REG_IN_MSB[SCH5627_NO_IN] = { + 0x22, 0x23, 0x24, 0x25, 0x189 }; +static const u16 SCH5627_REG_IN_LSN[SCH5627_NO_IN] = { + 0xE4, 0xE4, 0xE3, 0xE3, 0x18A }; +static const u16 SCH5627_REG_IN_HIGH_NIBBLE[SCH5627_NO_IN] = { + 1, 0, 1, 0, 1 }; +static const u16 SCH5627_REG_IN_FACTOR[SCH5627_NO_IN] = { + 10745, 3660, 9765, 10745, 3660 }; +static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { + "VCC", "VTT", "VBAT", "VTR", "V_IN" }; + +struct sch5627_data { + unsigned short addr; + struct device *hwmon_dev; + u8 temp_max[SCH5627_NO_TEMPS]; + u8 temp_crit[SCH5627_NO_TEMPS]; + u16 fan_min[SCH5627_NO_FANS]; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + u16 temp[SCH5627_NO_TEMPS]; + u16 fan[SCH5627_NO_FANS]; + u16 in[SCH5627_NO_IN]; +}; + +static struct platform_device *sch5627_pdev; + +/* Super I/O functions */ +static inline int superio_inb(int base, int reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static inline int superio_enter(int base) +{ + /* Don't step on other drivers' I/O space by accident */ + if (!request_muxed_region(base, 2, DRVNAME)) { + pr_err("I/O address 0x%04x already in use\n", base); + return -EBUSY; + } + + outb(SIO_UNLOCK_KEY, base); + + return 0; +} + +static inline void superio_select(int base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ + outb(SIO_LOCK_KEY, base); + release_region(base, 2); +} + +static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg) +{ + u8 val; + int i; + /* + * According to SMSC for the commands we use the maximum time for + * the EM to respond is 15 ms, but testing shows in practice it + * responds within 15-32 reads, so we first busy poll, and if + * that fails sleep a bit and try again until we are way past + * the 15 ms maximum response time. + */ + const int max_busy_polls = 64; + const int max_lazy_polls = 32; + + /* (Optional) Write-Clear the EC to Host Mailbox Register */ + val = inb(data->addr + 1); + outb(val, data->addr + 1); + + /* Set Mailbox Address Pointer to first location in Region 1 */ + outb(0x00, data->addr + 2); + outb(0x80, data->addr + 3); + + /* Write Request Packet Header */ + outb(0x02, data->addr + 4); /* Access Type: VREG read */ + outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */ + outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */ + + /* Write Address field */ + outb(reg & 0xff, data->addr + 6); + outb(reg >> 8, data->addr + 7); + + /* Execute the Random Access Command */ + outb(0x01, data->addr); /* Write 01h to the Host-to-EC register */ + + /* EM Interface Polling "Algorithm" */ + for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { + if (i >= max_busy_polls) + msleep(1); + /* Read Interrupt source Register */ + val = inb(data->addr + 8); + /* Write Clear the interrupt source bits */ + if (val) + outb(val, data->addr + 8); + /* Command Completed ? */ + if (val & 0x01) + break; + } + if (i == max_busy_polls + max_lazy_polls) { + pr_err("Max retries exceeded reading virtual " + "register 0x%04hx (%d)\n", reg, 1); + return -EIO; + } + + /* + * According to SMSC we may need to retry this, but sofar I've always + * seen this succeed in 1 try. + */ + for (i = 0; i < max_busy_polls; i++) { + /* Read EC-to-Host Register */ + val = inb(data->addr + 1); + /* Command Completed ? */ + if (val == 0x01) + break; + + if (i == 0) + pr_warn("EC reports: 0x%02x reading virtual register " + "0x%04hx\n", (unsigned int)val, reg); + } + if (i == max_busy_polls) { + pr_err("Max retries exceeded reading virtual " + "register 0x%04hx (%d)\n", reg, 2); + return -EIO; + } + + /* + * According to the SMSC app note we should now do: + * + * Set Mailbox Address Pointer to first location in Region 1 * + * outb(0x00, data->addr + 2); + * outb(0x80, data->addr + 3); + * + * But if we do that things don't work, so let's not. + */ + + /* Read Data from Mailbox */ + return inb(data->addr + 4); +} + +static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg) +{ + int lsb, msb; + + /* Read LSB first, this will cause the matching MSB to be latched */ + lsb = sch5627_read_virtual_reg(data, reg); + if (lsb < 0) + return lsb; + + msb = sch5627_read_virtual_reg(data, reg + 1); + if (msb < 0) + return msb; + + return lsb | (msb << 8); +} + +static int sch5627_read_virtual_reg12(struct sch5627_data *data, u16 msb_reg, + u16 lsn_reg, int high_nibble) +{ + int msb, lsn; + + /* Read MSB first, this will cause the matching LSN to be latched */ + msb = sch5627_read_virtual_reg(data, msb_reg); + if (msb < 0) + return msb; + + lsn = sch5627_read_virtual_reg(data, lsn_reg); + if (lsn < 0) + return lsn; + + if (high_nibble) + return (msb << 4) | (lsn >> 4); + else + return (msb << 4) | (lsn & 0x0f); +} + +static struct sch5627_data *sch5627_update_device(struct device *dev) +{ + struct sch5627_data *data = dev_get_drvdata(dev); + struct sch5627_data *ret = data; + int i, val; + + mutex_lock(&data->update_lock); + + /* Cache the values for 1 second */ + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < SCH5627_NO_TEMPS; i++) { + val = sch5627_read_virtual_reg12(data, + SCH5627_REG_TEMP_MSB[i], + SCH5627_REG_TEMP_LSN[i], + SCH5627_REG_TEMP_HIGH_NIBBLE[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i] = val; + } + + for (i = 0; i < SCH5627_NO_FANS; i++) { + val = sch5627_read_virtual_reg16(data, + SCH5627_REG_FAN[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->fan[i] = val; + } + + for (i = 0; i < SCH5627_NO_IN; i++) { + val = sch5627_read_virtual_reg12(data, + SCH5627_REG_IN_MSB[i], + SCH5627_REG_IN_LSN[i], + SCH5627_REG_IN_HIGH_NIBBLE[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->in[i] = val; + } + + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int __devinit sch5627_read_limits(struct sch5627_data *data) +{ + int i, val; + + for (i = 0; i < SCH5627_NO_TEMPS; i++) { + /* + * Note what SMSC calls ABS, is what lm_sensors calls max + * (aka high), and HIGH is what lm_sensors calls crit. + */ + val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_ABS[i]); + if (val < 0) + return val; + data->temp_max[i] = val; + + val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_HIGH[i]); + if (val < 0) + return val; + data->temp_crit[i] = val; + } + for (i = 0; i < SCH5627_NO_FANS; i++) { + val = sch5627_read_virtual_reg16(data, SCH5627_REG_FAN_MIN[i]); + if (val < 0) + return val; + data->fan_min[i] = val; + } + + return 0; +} + +static int reg_to_temp(u16 reg) +{ + return (reg * 625) / 10 - 64000; +} + +static int reg_to_temp_limit(u8 reg) +{ + return (reg - 64) * 1000; +} + +static int reg_to_rpm(u16 reg) +{ + if (reg == 0) + return -EIO; + if (reg == 0xffff) + return 0; + + return 5400540 / reg; +} + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); +} + +static ssize_t show_temp(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = reg_to_temp(data->temp[attr->index]); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->temp[attr->index] == 0); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = dev_get_drvdata(dev); + int val; + + val = reg_to_temp_limit(data->temp_max[attr->index]); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_crit(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = dev_get_drvdata(dev); + int val; + + val = reg_to_temp_limit(data->temp_crit[attr->index]); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = reg_to_rpm(data->fan[attr->index]); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->fan[attr->index] == 0xffff); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = dev_get_drvdata(dev); + int val = reg_to_rpm(data->fan_min[attr->index]); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST( + data->in[attr->index] * SCH5627_REG_IN_FACTOR[attr->index], + 10000); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in_label(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return snprintf(buf, PAGE_SIZE, "%s\n", + SCH5627_IN_LABELS[attr->index]); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_temp_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_temp_fault, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_max, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_max, S_IRUGO, show_temp_max, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_max, S_IRUGO, show_temp_max, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_crit, S_IRUGO, show_temp_crit, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_crit, S_IRUGO, show_temp_crit, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO, show_fan_min, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO, show_fan_min, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO, show_fan_min, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_min, S_IRUGO, show_fan_min, NULL, 3); + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_in_label, NULL, 3); + +static struct attribute *sch5627_attributes[] = { + &dev_attr_name.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + &sensor_dev_attr_temp7_fault.dev_attr.attr, + &sensor_dev_attr_temp8_fault.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp7_crit.dev_attr.attr, + &sensor_dev_attr_temp8_crit.dev_attr.attr, + + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + /* No in4_label as in4 is a generic input pin */ + + NULL +}; + +static const struct attribute_group sch5627_group = { + .attrs = sch5627_attributes, +}; + +static int sch5627_remove(struct platform_device *pdev) +{ + struct sch5627_data *data = platform_get_drvdata(pdev); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + sysfs_remove_group(&pdev->dev.kobj, &sch5627_group); + platform_set_drvdata(pdev, NULL); + kfree(data); + + return 0; +} + +static int __devinit sch5627_probe(struct platform_device *pdev) +{ + struct sch5627_data *data; + int err, build_code, build_id, hwmon_rev, val; + + data = kzalloc(sizeof(struct sch5627_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + val = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_ID); + if (val < 0) { + err = val; + goto error; + } + if (val != SCH5627_HWMON_ID) { + pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon", + val, SCH5627_HWMON_ID); + err = -ENODEV; + goto error; + } + + val = sch5627_read_virtual_reg(data, SCH5627_REG_COMPANY_ID); + if (val < 0) { + err = val; + goto error; + } + if (val != SCH5627_COMPANY_ID) { + pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company", + val, SCH5627_COMPANY_ID); + err = -ENODEV; + goto error; + } + + val = sch5627_read_virtual_reg(data, SCH5627_REG_PRIMARY_ID); + if (val < 0) { + err = val; + goto error; + } + if (val != SCH5627_PRIMARY_ID) { + pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary", + val, SCH5627_PRIMARY_ID); + err = -ENODEV; + goto error; + } + + build_code = sch5627_read_virtual_reg(data, SCH5627_REG_BUILD_CODE); + if (build_code < 0) { + err = build_code; + goto error; + } + + build_id = sch5627_read_virtual_reg16(data, SCH5627_REG_BUILD_ID); + if (build_id < 0) { + err = build_id; + goto error; + } + + hwmon_rev = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_REV); + if (hwmon_rev < 0) { + err = hwmon_rev; + goto error; + } + + val = sch5627_read_virtual_reg(data, SCH5627_REG_CTRL); + if (val < 0) { + err = val; + goto error; + } + if (!(val & 0x01)) { + pr_err("hardware monitoring not enabled\n"); + err = -ENODEV; + goto error; + } + + /* + * Read limits, we do this only once as reading a register on + * the sch5627 is quite expensive (and they don't change). + */ + err = sch5627_read_limits(data); + if (err) + goto error; + + pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", + build_code, build_id, hwmon_rev); + + /* Register sysfs interface files */ + err = sysfs_create_group(&pdev->dev.kobj, &sch5627_group); + if (err) + goto error; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto error; + } + + return 0; + +error: + sch5627_remove(pdev); + return err; +} + +static int __init sch5627_find(int sioaddr, unsigned short *address) +{ + u8 devid; + int err = superio_enter(sioaddr); + if (err) + return err; + + devid = superio_inb(sioaddr, SIO_REG_DEVID); + if (devid != SIO_SCH5627_ID) { + pr_debug("Unsupported device id: 0x%02x\n", + (unsigned int)devid); + err = -ENODEV; + goto exit; + } + + superio_select(sioaddr, SIO_SCH5627_EM_LD); + + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { + pr_warn("Device not activated\n"); + err = -ENODEV; + goto exit; + } + + /* + * Warning the order of the low / high byte is the other way around + * as on most other superio devices!! + */ + *address = superio_inb(sioaddr, SIO_REG_ADDR) | + superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; + if (*address == 0) { + pr_warn("Base address not set\n"); + err = -ENODEV; + goto exit; + } + + pr_info("Found %s chip at %#hx\n", DEVNAME, *address); +exit: + superio_exit(sioaddr); + return err; +} + +static int __init sch5627_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + REGION_LENGTH - 1, + .flags = IORESOURCE_IO, + }; + int err; + + sch5627_pdev = platform_device_alloc(DRVNAME, address); + if (!sch5627_pdev) + return -ENOMEM; + + res.name = sch5627_pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + + err = platform_device_add_resources(sch5627_pdev, &res, 1); + if (err) { + pr_err("Device resource addition failed\n"); + goto exit_device_put; + } + + err = platform_device_add(sch5627_pdev); + if (err) { + pr_err("Device addition failed\n"); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(sch5627_pdev); + + return err; +} + +static struct platform_driver sch5627_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = sch5627_probe, + .remove = sch5627_remove, +}; + +static int __init sch5627_init(void) +{ + int err = -ENODEV; + unsigned short address; + + if (sch5627_find(0x4e, &address) && sch5627_find(0x2e, &address)) + goto exit; + + err = platform_driver_register(&sch5627_driver); + if (err) + goto exit; + + err = sch5627_device_add(address); + if (err) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&sch5627_driver); +exit: + return err; +} + +static void __exit sch5627_exit(void) +{ + platform_device_unregister(sch5627_pdev); + platform_driver_unregister(&sch5627_driver); +} + +MODULE_DESCRIPTION("SMSC SCH5627 Hardware Monitoring Driver"); +MODULE_AUTHOR("Hans de Goede (hdegoede@redhat.com)"); +MODULE_LICENSE("GPL"); + +module_init(sch5627_init); +module_exit(sch5627_exit); -- cgit v1.2.3 From 8c22a8f57516275afcd81c84f3724ac08cf6aa7b Mon Sep 17 00:00:00 2001 From: Dirk Eibach Date: Mon, 21 Mar 2011 17:59:36 +0100 Subject: hwmon: Add support for Texas Instruments ADS1015 Signed-off-by: Dirk Eibach Signed-off-by: Jean Delvare --- .../devicetree/bindings/hwmon/ads1015.txt | 29 +++ Documentation/hwmon/ads1015 | 67 +++++ MAINTAINERS | 8 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ads1015.c | 282 +++++++++++++++++++++ include/linux/i2c/ads1015.h | 28 ++ 7 files changed, 425 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/ads1015.txt create mode 100644 Documentation/hwmon/ads1015 create mode 100644 drivers/hwmon/ads1015.c create mode 100644 include/linux/i2c/ads1015.h (limited to 'MAINTAINERS') diff --git a/Documentation/devicetree/bindings/hwmon/ads1015.txt b/Documentation/devicetree/bindings/hwmon/ads1015.txt new file mode 100644 index 000000000000..0f30616384c5 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ads1015.txt @@ -0,0 +1,29 @@ +ADS1015 (I2C) + +This device is a 12-bit A-D converter with 4 inputs. + +The inputs can be used single ended or in certain differential combinations. + +For configuration all possible combinations are mapped to 8 channels: +0: Voltage over AIN0 and AIN1. +1: Voltage over AIN0 and AIN3. +2: Voltage over AIN1 and AIN3. +3: Voltage over AIN2 and AIN3. +4: Voltage over AIN0 and GND. +5: Voltage over AIN1 and GND. +6: Voltage over AIN2 and GND. +7: Voltage over AIN3 and GND. + +Optional properties: + + - exported-channels : exported_channels is a bitmask that specifies which + channels should be accessable by the user. + +Example: +ads1015@49 { + compatible = "ti,ads1015"; + reg = <0x49>; + exported-channels = <0x14>; +}; + +In this example only channel 2 and 4 would be accessable by the user. diff --git a/Documentation/hwmon/ads1015 b/Documentation/hwmon/ads1015 new file mode 100644 index 000000000000..56ee7977b1a8 --- /dev/null +++ b/Documentation/hwmon/ads1015 @@ -0,0 +1,67 @@ +Kernel driver ads1015 +===================== + +Supported chips: + * Texas Instruments ADS1015 + Prefix: 'ads1015' + Datasheet: Publicly available at the Texas Instruments website : + http://focus.ti.com/lit/ds/symlink/ads1015.pdf + +Authors: + Dirk Eibach, Guntermann & Drunck GmbH + +Description +----------- + +This driver implements support for the Texas Instruments ADS1015. + +This device is a 12-bit A-D converter with 4 inputs. + +The inputs can be used single ended or in certain differential combinations. + +The inputs can be exported to 8 sysfs input files in0_input - in7_input: +in0: Voltage over AIN0 and AIN1. +in1: Voltage over AIN0 and AIN3. +in2: Voltage over AIN1 and AIN3. +in3: Voltage over AIN2 and AIN3. +in4: Voltage over AIN0 and GND. +in5: Voltage over AIN1 and GND. +in6: Voltage over AIN2 and GND. +in7: Voltage over AIN3 and GND. + +Which inputs are exported can be configured using platform data or devicetree. + +By default all inputs are exported. + +Platform Data +------------- + +In linux/i2c/ads1015.h platform data is defined as: + +struct ads1015_platform_data { + unsigned int exported_channels; +}; + +exported_channels is a bitmask that specifies which inputs should be exported. + +Example: +struct ads1015_platform_data data = { + .exported_channels = (1 << 2) | (1 << 4) +}; + +In this case only in2_input and in4_input would be created. + +Devicetree +---------- + +The ads1015 node may have an "exported-channels" property. +exported_channels is a bitmask that specifies which inputs should be exported. + +Example: +ads1015@49 { + compatible = "ti,ads1015"; + reg = <0x49>; + exported-channels = < 0x14 >; +}; + +In this case only in2_input and in4_input would be created. diff --git a/MAINTAINERS b/MAINTAINERS index b8a123f7e810..38077a656820 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -365,6 +365,14 @@ W: http://wiki-analog.com/ADP8860 S: Supported F: drivers/video/backlight/adp8860_bl.c +ADS1015 HARDWARE MONITOR DRIVER +M: Dirk Eibach +L: lm-sensors@lm-sensors.org +S: Maintained +F: Documentation/hwmon/ads1015 +F: drivers/hwmon/ads1015.c +F: include/linux/i2c/ads1015.h + ADT746X FAN DRIVER M: Colin Leroy S: Maintained diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6fad9f082f67..e4bd13b3cd8b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -968,6 +968,16 @@ config SENSORS_SCH5627 This driver can also be built as a module. If so, the module will be called sch5627. +config SENSORS_ADS1015 + tristate "Texas Instruments ADS1015" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS1015 + 12-bit 4-input ADC device. + + This driver can also be built as a module. If so, the module + will be called ads1015. + config SENSORS_ADS7828 tristate "Texas Instruments ADS7828" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4d0122c52219..54ca5939d028 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c new file mode 100644 index 000000000000..9e1585c3152e --- /dev/null +++ b/drivers/hwmon/ads1015.c @@ -0,0 +1,282 @@ +/* + * ads1015.c - lm_sensors driver for ads1015 12-bit 4-input ADC + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH + * + * Based on the ads7828 driver by Steve Hardy. + * + * Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads1015.pdf + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ADS1015 registers */ +enum { + ADS1015_CONVERSION = 0, + ADS1015_CONFIG = 1, +}; + +/* PGA fullscale voltages in mV */ +static const unsigned int fullscale_table[8] = { + 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; + +#define ADS1015_CONFIG_CHANNELS 8 +#define ADS1015_DEFAULT_CHANNELS 0xff + +struct ads1015_data { + struct device *hwmon_dev; + struct mutex update_lock; /* mutex protect updates */ + struct attribute *attr_table[ADS1015_CONFIG_CHANNELS + 1]; + struct attribute_group attr_group; +}; + +static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg) +{ + s32 data = i2c_smbus_read_word_data(client, reg); + + return (data < 0) ? data : swab16(data); +} + +static s32 ads1015_write_reg(struct i2c_client *client, unsigned int reg, + u16 val) +{ + return i2c_smbus_write_word_data(client, reg, swab16(val)); +} + +static int ads1015_read_value(struct i2c_client *client, unsigned int channel, + int *value) +{ + u16 config; + s16 conversion; + unsigned int pga; + int fullscale; + unsigned int k; + struct ads1015_data *data = i2c_get_clientdata(client); + int res; + + mutex_lock(&data->update_lock); + + /* get fullscale voltage */ + res = ads1015_read_reg(client, ADS1015_CONFIG); + if (res < 0) + goto err_unlock; + config = res; + pga = (config >> 9) & 0x0007; + fullscale = fullscale_table[pga]; + + /* set channel and start single conversion */ + config &= ~(0x0007 << 12); + config |= (1 << 15) | (1 << 8) | (channel & 0x0007) << 12; + + /* wait until conversion finished */ + res = ads1015_write_reg(client, ADS1015_CONFIG, config); + if (res < 0) + goto err_unlock; + for (k = 0; k < 5; ++k) { + msleep(1); + res = ads1015_read_reg(client, ADS1015_CONFIG); + if (res < 0) + goto err_unlock; + config = res; + if (config & (1 << 15)) + break; + } + if (k == 5) { + res = -EIO; + goto err_unlock; + } + + res = ads1015_read_reg(client, ADS1015_CONVERSION); + if (res < 0) + goto err_unlock; + conversion = res; + + mutex_unlock(&data->update_lock); + + *value = DIV_ROUND_CLOSEST(conversion * fullscale, 0x7ff0); + + return 0; + +err_unlock: + mutex_unlock(&data->update_lock); + return res; +} + +/* sysfs callback function */ +static ssize_t show_in(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + int in; + int res; + + res = ads1015_read_value(client, attr->index, &in); + + return (res < 0) ? res : sprintf(buf, "%d\n", in); +} + +#define in_reg(offset)\ +static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\ + NULL, offset) + +in_reg(0); +in_reg(1); +in_reg(2); +in_reg(3); +in_reg(4); +in_reg(5); +in_reg(6); +in_reg(7); + +static struct attribute *all_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, +}; + +/* + * Driver interface + */ + +static int ads1015_remove(struct i2c_client *client) +{ + struct ads1015_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &data->attr_group); + kfree(data); + return 0; +} + +static unsigned int ads1015_get_exported_channels(struct i2c_client *client) +{ + struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); +#ifdef CONFIG_OF + struct device_node *np = client->dev.of_node; + const __be32 *of_channels; + int of_channels_size; +#endif + + /* prefer platform data */ + if (pdata) + return pdata->exported_channels; + +#ifdef CONFIG_OF + /* fallback on OF */ + of_channels = of_get_property(np, "exported-channels", + &of_channels_size); + if (of_channels && (of_channels_size == sizeof(*of_channels))) + return be32_to_cpup(of_channels); +#endif + + /* fallback on default configuration */ + return ADS1015_DEFAULT_CHANNELS; +} + +static int ads1015_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ads1015_data *data; + int err; + unsigned int exported_channels; + unsigned int k; + unsigned int n = 0; + + data = kzalloc(sizeof(struct ads1015_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* build sysfs attribute group */ + data->attr_group.attrs = data->attr_table; + exported_channels = ads1015_get_exported_channels(client); + for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) { + if (!(exported_channels & (1<attr_table[n++] = all_attributes[k]; + } + err = sysfs_create_group(&client->dev.kobj, &data->attr_group); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &data->attr_group); +exit_free: + kfree(data); +exit: + return err; +} + +static const struct i2c_device_id ads1015_id[] = { + { "ads1015", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads1015_id); + +static struct i2c_driver ads1015_driver = { + .driver = { + .name = "ads1015", + }, + .probe = ads1015_probe, + .remove = ads1015_remove, + .id_table = ads1015_id, +}; + +static int __init sensors_ads1015_init(void) +{ + return i2c_add_driver(&ads1015_driver); +} + +static void __exit sensors_ads1015_exit(void) +{ + i2c_del_driver(&ads1015_driver); +} + +MODULE_AUTHOR("Dirk Eibach "); +MODULE_DESCRIPTION("ADS1015 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_ads1015_init); +module_exit(sensors_ads1015_exit); diff --git a/include/linux/i2c/ads1015.h b/include/linux/i2c/ads1015.h new file mode 100644 index 000000000000..8541c6acfafd --- /dev/null +++ b/include/linux/i2c/ads1015.h @@ -0,0 +1,28 @@ +/* + * Platform Data for ADS1015 12-bit 4-input ADC + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH + * + * 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. + */ + +#ifndef LINUX_ADS1015_H +#define LINUX_ADS1015_H + +struct ads1015_platform_data { + unsigned int exported_channels; +}; + +#endif /* LINUX_ADS1015_H */ -- cgit v1.2.3 From 7e8970e1d5ae9237d00909599e70f85fce1fc489 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: Kconfig and Makefile Add the OMAP3 ISP driver to the kernel build system. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 6 ++++++ drivers/media/video/Kconfig | 13 +++++++++++++ drivers/media/video/Makefile | 2 ++ drivers/media/video/omap3isp/Makefile | 13 +++++++++++++ include/linux/Kbuild | 1 + 5 files changed, 35 insertions(+) create mode 100644 drivers/media/video/omap3isp/Makefile (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index f1bc3dc6b369..1034f4a67347 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4556,6 +4556,12 @@ L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c +OMAP IMAGE SIGNAL PROCESSOR (ISP) +M: Laurent Pinchart +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/video/omap3isp/* + OMAP USB SUPPORT M: Felipe Balbi M: David Brownell diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e2f5a69aa400..4498b944dec8 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -746,6 +746,19 @@ config VIDEO_NOON010PC30 ---help--- This driver supports NOON010PC30 CIF camera from Siliconfile +config VIDEO_OMAP3 + tristate "OMAP 3 Camera support (EXPERIMENTAL)" + select OMAP_IOMMU + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL + ---help--- + Driver for an OMAP 3 camera controller. + +config VIDEO_OMAP3_DEBUG + bool "OMAP 3 Camera debug messages" + depends on VIDEO_OMAP3 + ---help--- + Enable debug messages on OMAP 3 camera controller driver. + config SOC_CAMERA tristate "SoC camera support" depends on VIDEO_V4L2 && HAS_DMA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 7ea65163090e..ace5d8b57221 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -129,6 +129,8 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o +obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ + obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile new file mode 100644 index 000000000000..b1b344774ae7 --- /dev/null +++ b/drivers/media/video/omap3isp/Makefile @@ -0,0 +1,13 @@ +# Makefile for OMAP3 ISP driver + +ifdef CONFIG_VIDEO_OMAP3_DEBUG +EXTRA_CFLAGS += -DDEBUG +endif + +omap3-isp-objs += \ + isp.o ispqueue.o ispvideo.o \ + ispcsiphy.o ispccp2.o ispcsi2.o \ + ispccdc.o isppreview.o ispresizer.o \ + ispstat.o isph3a_aewb.o isph3a_af.o isphist.o + +obj-$(CONFIG_VIDEO_OMAP3) += omap3-isp.o diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 43918a34db9f..75cf611641e6 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -277,6 +277,7 @@ header-y += nfsacl.h header-y += nl80211.h header-y += nubus.h header-y += nvram.h +header-y += omap3isp.h header-y += omapfb.h header-y += oom.h header-y += param.h -- cgit v1.2.3 From d958c62c0cf43bb9c7dcd9833a35335ae72856f7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 23 Mar 2011 08:22:41 +0900 Subject: MAINTAINERS: Add file pattern for fb dt bindings. Now that Documentation/devicetree/bindings/fb/ exists, make sure it's also matched by the default framebuffer entry. Signed-off-by: Paul Mundt --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 5b28ed0b60c6..d71a5c8649cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2633,6 +2633,7 @@ Q: http://patchwork.kernel.org/project/linux-fbdev/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/fbdev-2.6.git S: Orphan F: Documentation/fb/ +F: Documentation/devicetree/bindings/fb/ F: drivers/video/ F: include/video/ F: include/linux/fb.h -- cgit v1.2.3 From 56be1416453c31d32f984328b5193489ab63ffcf Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 23 Mar 2011 08:29:07 +0900 Subject: MAINTAINERS: de-orphan fbdev. It's been a few kernel versions now without any unexpected surprises, so tentatively bump up the support status. Signed-off-by: Paul Mundt --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index d71a5c8649cf..c7acfb0ef9ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2627,11 +2627,12 @@ F: drivers/net/wan/dlci.c F: drivers/net/wan/sdla.c FRAMEBUFFER LAYER +M: Paul Mundt L: linux-fbdev@vger.kernel.org W: http://linux-fbdev.sourceforge.net/ Q: http://patchwork.kernel.org/project/linux-fbdev/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/fbdev-2.6.git -S: Orphan +S: Maintained F: Documentation/fb/ F: Documentation/devicetree/bindings/fb/ F: drivers/video/ -- cgit v1.2.3 From b153da600c2755588aad1977349d14eab9c2a959 Mon Sep 17 00:00:00 2001 From: Harry Wei Date: Tue, 22 Mar 2011 16:34:25 -0700 Subject: MAINTAINERS: add topgit for T: At least one tree: ARM/EZX SMARTPHONES (A780, A910, A1200, E680, ROKR E2 and ROKR E6) is available via topgit. Add mention of topgit in the MAINTAINERS description section. Signed-off-by: Harry Wei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 53e315e777ff..d78dd3e51e3b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -72,7 +72,7 @@ Descriptions of section entries: L: Mailing list that is relevant to this area W: Web-page with status/info Q: Patchwork web based patch tracking system site - T: SCM tree type and location. Type is one of: git, hg, quilt, stgit. + T: SCM tree type and location. Type is one of: git, hg, quilt, stgit, topgit. S: Status, one of the following: Supported: Someone is actually paid to look after this. Maintained: Someone actually looks after it. -- cgit v1.2.3 From 706e69d67652686d45d431b36174aefa3760aa39 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:26 -0700 Subject: MAINTAINERS: quote non-alphanumeric email addresses Add quotes around email address with periods and commas. So they don't explode when pasted into certain email clients. Signed-off-by: Joe Perches Acked-by: Harry Wei Cc: "Mark F. Brown" Cc: "Gustavo F. Padovan" Cc: "Stephen M. Cameron" Cc: "Lee, Chun-Yi" Acked-by: Harry Wei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index d78dd3e51e3b..3dc158b98ad7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1078,7 +1078,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/TETON BGA MACHINE SUPPORT -M: Mark F. Brown +M: "Mark F. Brown" L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained @@ -1482,7 +1482,7 @@ F: drivers/mtd/devices/block2mtd.c BLUETOOTH DRIVERS M: Marcel Holtmann -M: Gustavo F. Padovan +M: "Gustavo F. Padovan" L: linux-bluetooth@vger.kernel.org W: http://www.bluez.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-2.6.git @@ -1491,7 +1491,7 @@ F: drivers/bluetooth/ BLUETOOTH SUBSYSTEM M: Marcel Holtmann -M: Gustavo F. Padovan +M: "Gustavo F. Padovan" L: linux-bluetooth@vger.kernel.org W: http://www.bluez.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-2.6.git @@ -2950,7 +2950,7 @@ F: Documentation/blockdev/cpqarray.txt F: drivers/block/cpqarray.* HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa) -M: Stephen M. Cameron +M: "Stephen M. Cameron" L: iss_storagedev@hp.com S: Supported F: Documentation/scsi/hpsa.txt @@ -4232,7 +4232,7 @@ F: Documentation/serial/moxa-smartio F: drivers/char/mxser.* MSI LAPTOP SUPPORT -M: Lee, Chun-Yi +M: "Lee, Chun-Yi" L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/msi-laptop.c -- cgit v1.2.3 From 45b4e0d54bdbef19b5f753421851bdc91007c29b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:27 -0700 Subject: MAINTAINERS: update ADP5520 pattern Typo in path. Signed-off-by: Joe Perches Acked-by: Michael Hennerich Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 3dc158b98ad7..4212dd172f85 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -346,7 +346,7 @@ W: http://wiki-analog.com/ADP5520 S: Supported F: drivers/mfd/adp5520.c F: drivers/video/backlight/adp5520_bl.c -F: drivers/led/leds-adp5520.c +F: drivers/leds/leds-adp5520.c F: drivers/gpio/adp5520-gpio.c F: drivers/input/keyboard/adp5520-keys.c -- cgit v1.2.3 From a3f531ac556d77bca39bfd454b8b151f562c30e4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:28 -0700 Subject: MAINTAINERS: change web links from wiki-analog to wiki.analog wiki-analog doesn't seem to work, but wiki.analog does. Signed-off-by: Joe Perches Cc: Mike Frysinger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4212dd172f85..dc23f20684fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -288,35 +288,35 @@ F: sound/pci/ad1889.* AD525X ANALOG DEVICES DIGITAL POTENTIOMETERS DRIVER M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/AD5254 +W: http://wiki.analog.com/AD5254 S: Supported F: drivers/misc/ad525x_dpot.c AD5398 CURRENT REGULATOR DRIVER (AD5398/AD5821) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/AD5398 +W: http://wiki.analog.com/AD5398 S: Supported F: drivers/regulator/ad5398.c AD714X CAPACITANCE TOUCH SENSOR DRIVER (AD7142/3/7/8/7A) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/AD7142 +W: http://wiki.analog.com/AD7142 S: Supported F: drivers/input/misc/ad714x.c AD7877 TOUCHSCREEN DRIVER M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/AD7877 +W: http://wiki.analog.com/AD7877 S: Supported F: drivers/input/touchscreen/ad7877.c AD7879 TOUCHSCREEN DRIVER (AD7879/AD7889) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/AD7879 +W: http://wiki.analog.com/AD7879 S: Supported F: drivers/input/touchscreen/ad7879.c @@ -342,7 +342,7 @@ F: drivers/net/wireless/adm8211.* ADP5520 BACKLIGHT DRIVER WITH IO EXPANDER (ADP5520/ADP5501) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/ADP5520 +W: http://wiki.analog.com/ADP5520 S: Supported F: drivers/mfd/adp5520.c F: drivers/video/backlight/adp5520_bl.c @@ -353,7 +353,7 @@ F: drivers/input/keyboard/adp5520-keys.c ADP5588 QWERTY KEYPAD AND IO EXPANDER DRIVER (ADP5588/ADP5587) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/ADP5588 +W: http://wiki.analog.com/ADP5588 S: Supported F: drivers/input/keyboard/adp5588-keys.c F: drivers/gpio/adp5588-gpio.c @@ -361,7 +361,7 @@ F: drivers/gpio/adp5588-gpio.c ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/ADP8860 +W: http://wiki.analog.com/ADP8860 S: Supported F: drivers/video/backlight/adp8860_bl.c @@ -388,7 +388,7 @@ F: drivers/hwmon/adt7475.c ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346) M: Michael Hennerich L: device-driver-devel@blackfin.uclinux.org -W: http://wiki-analog.com/ADXL345 +W: http://wiki.analog.com/ADXL345 S: Supported F: drivers/input/misc/adxl34x.c @@ -528,7 +528,7 @@ F: drivers/infiniband/hw/amso1100/ ANALOG DEVICES INC ASOC CODEC DRIVERS L: device-driver-devel@blackfin.uclinux.org L: alsa-devel@alsa-project.org (moderated for non-subscribers) -W: http://wiki-analog.com/ +W: http://wiki.analog.com/ S: Supported F: sound/soc/codecs/ad1* F: sound/soc/codecs/adau* -- cgit v1.2.3 From 0ddf20e3743d6f1bb1f417749f10203f83664415 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:29 -0700 Subject: MAINTAINERS: remove ASOC CODEC DRIVERS files not in tree Remove these patterns until such files are actually in the tree. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index dc23f20684fb..56e6d845d318 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -531,8 +531,6 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://wiki.analog.com/ S: Supported F: sound/soc/codecs/ad1* -F: sound/soc/codecs/adau* -F: sound/soc/codecs/adav* F: sound/soc/codecs/ssm* ANALOG DEVICES INC ASOC DRIVERS -- cgit v1.2.3 From e801dc5cf4af61d19c02fc7ea5e884fa07ce6264 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:29 -0700 Subject: MAINTAINERS: add missing : after HR Timers F tag Signed-off-by: Joe Perches Acked-by: Harry Wei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 56e6d845d318..4e5f4c990972 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3005,7 +3005,7 @@ F: kernel/hrtimer.c F: kernel/time/clockevents.c F: kernel/time/tick*.* F: kernel/time/timer_*.c -F include/linux/clockevents.h +F: include/linux/clockevents.h F: include/linux/hrtimer.h HIGH-SPEED SCC DRIVER FOR AX.25 -- cgit v1.2.3 From 6e08bbb8830b3ab1b583c9a2b30a45968f946c7a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:30 -0700 Subject: MAINTAINERS: remove unnecessary linux-kernel@vger.kernel.org entries CC'ing lkml is the default and doesn't need separate entries. Signed-off-by: Joe Perches Acked-by: Harry Wei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 5 ----- 1 file changed, 5 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4e5f4c990972..64b94b21ebf0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2824,7 +2824,6 @@ F: include/linux/gigaset_dev.h GPIO SUBSYSTEM M: Grant Likely -L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.secretlab.ca/git/linux-2.6.git F: Documentation/gpio/gpio.txt @@ -2847,7 +2846,6 @@ F: drivers/platform/x86/hdaps.c HWPOISON MEMORY FAILURE HANDLING M: Andi Kleen L: linux-mm@kvack.org -L: linux-kernel@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-mce-2.6.git hwpoison S: Maintained F: mm/memory-failure.c @@ -4725,7 +4723,6 @@ F: drivers/i2c/busses/i2c-pasemi.c PADATA PARALLEL EXECUTION MECHANISM M: Steffen Klassert -L: linux-kernel@vger.kernel.org L: linux-crypto@vger.kernel.org S: Maintained F: kernel/padata.c @@ -4875,7 +4872,6 @@ F: include/crypto/pcrypt.h PER-CPU MEMORY ALLOCATOR M: Tejun Heo M: Christoph Lameter -L: linux-kernel@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu.git S: Maintained F: include/linux/percpu*.h @@ -6923,7 +6919,6 @@ F: sound/soc/codecs/wm* WORKQUEUE M: Tejun Heo -L: linux-kernel@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git S: Maintained F: include/linux/workqueue.h -- cgit v1.2.3 From 4fa2651db4635d0fbb9ecc875a79072a00c3c3a7 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:31 -0700 Subject: MAINTAINERS: update clkdev location Commit 6d803ba736a ("ARM: 6483/1: arm & sh: factorised duplicated clkdev.c") moved it to a separate directory. Signed-off-by: Joe Perches Cc: Russell King Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 64b94b21ebf0..450c2b92ad48 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -695,8 +695,8 @@ S: Maintained ARM/CLKDEV SUPPORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -F: arch/arm/common/clkdev.c F: arch/arm/include/asm/clkdev.h +F: drivers/clk/clkdev.c ARM/COMPULAB CM-X270/EM-X270 and CM-X300 MACHINE SUPPORT M: Mike Rapoport -- cgit v1.2.3 From 2a8374492b2301275debe1d378a12619a96a217e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:32 -0700 Subject: MAINTAINERS: update media path Commit 52b661449ae ("[media] rc: Rename remote controller type to rc_type instead of ir_type") moved it around. Signed-off-by: Joe Perches Cc: Maxim Levitsky Acked-by: Harry Wei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 450c2b92ad48..c2c0fd0f726b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2479,8 +2479,7 @@ F: include/linux/cb710.h ENE KB2426 (ENE0100/ENE020XX) INFRARED RECEIVER M: Maxim Levitsky S: Maintained -F: drivers/media/IR/ene_ir.c -F: drivers/media/IR/ene_ir.h +F: drivers/media/rc/ene_ir.* EPSON 1355 FRAMEBUFFER DRIVER M: Christopher Hoover -- cgit v1.2.3 From c45e98a274fd5cc5f270b06a0f224322bbed2d40 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:33 -0700 Subject: MAINTAINERS: remove IEEE1394 entry commit 66fa12c571d ("ieee1394: remove the old IEEE 1394 driver stack") removed the code, remove the MAINTAINERS entry. Signed-off-by: Joe Perches Cc: Stefan Richter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 9 --------- 1 file changed, 9 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index c2c0fd0f726b..ca02f356494b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3175,15 +3175,6 @@ L: linux-pm@lists.linux-foundation.org S: Supported F: drivers/idle/i7300_idle.c -IEEE 1394 SUBSYSTEM -M: Stefan Richter -L: linux1394-devel@lists.sourceforge.net -W: http://ieee1394.wiki.kernel.org/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git -S: Obsolete -F: Documentation/debugging-via-ohci1394.txt -F: drivers/ieee1394/ - IEEE 802.15.4 SUBSYSTEM M: Dmitry Eremin-Solenikov M: Sergey Lapin -- cgit v1.2.3 From 3a83ea6ecd31cbf4330b2c0a616e3d2539d5b616 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:34 -0700 Subject: MAINTAINERS: remove unused TIMEKEEPING timekeeping.h Commit 88606e80da0 ("MAINTAINERS: Update timer related entries") added a file pattern that didn't actually exist. Signed-off-by: Joe Perches Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index ca02f356494b..018a033e808d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5420,7 +5420,6 @@ S: Supported F: include/linux/clocksource.h F: include/linux/time.h F: include/linux/timex.h -F: include/linux/timekeeping.h F: kernel/time/clocksource.c F: kernel/time/time*.c F: kernel/time/ntp.c -- cgit v1.2.3 From 390889b6da931096aa6d8d8bb013f75ad711c0f4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:34 -0700 Subject: MAINTAINERS: update SCX200 file pattern commit 3b3da9d25ae ("x86: Move scx200 to platform") moved it, convert the pattern too. Signed-off-by: Joe Perches Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 018a033e808d..549d43ba0e9a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5507,7 +5507,7 @@ SCx200 CPU SUPPORT M: Jim Cromie S: Odd Fixes F: Documentation/i2c/busses/scx200_acb -F: arch/x86/kernel/scx200_32.c +F: arch/x86/platform/scx200/ F: drivers/watchdog/scx200_wdt.c F: drivers/i2c/busses/scx200* F: drivers/mtd/maps/scx200_docflash.c -- cgit v1.2.3 From 4e850855d747b5a8c7c54d90479b33f38d79726a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:35 -0700 Subject: MAINTAINERS: remove SHARP LH7A40X section commit 82e6923e186 ("ARM: lh7a40x: remove unmaintained platform support") removed support, remove it from MAINTAINERS. Signed-off-by: Joe Perches Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 549d43ba0e9a..7b6952ccebca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5651,17 +5651,6 @@ M: Robin Holt S: Maintained F: drivers/misc/sgi-xp/ -SHARP LH SUPPORT (LH7952X & LH7A40X) -M: Marc Singer -W: http://projects.buici.com/arm -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained -F: Documentation/arm/Sharp-LH/ADC-LH7-Touchscreen -F: arch/arm/mach-lh7a40x/ -F: drivers/tty/serial/serial_lh7a40x.c -F: drivers/usb/gadget/lh7a40* -F: drivers/usb/host/ohci-lh7a40* - SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown L: sfi-devel@simplefirmware.org -- cgit v1.2.3 From 943fc810bdc9c0fce5cc66bdb65b42d699241aa6 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:36 -0700 Subject: MAINTAINERS: update SFI pattern commit 937f961a653 ("x86: Move sfi to platform") moved the files, update the pattern. Signed-off-by: Joe Perches Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 7b6952ccebca..8b97e46f8b0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5657,7 +5657,7 @@ L: sfi-devel@simplefirmware.org W: http://simplefirmware.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-sfi-2.6.git S: Supported -F: arch/x86/kernel/*sfi* +F: arch/x86/platform/sfi/ F: drivers/sfi/ F: include/linux/sfi*.h -- cgit v1.2.3 From f1d2313025c92c25972372d3688d894476dcd61e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:37 -0700 Subject: MAINTAINERS: uSB SE401 moved to staging, update pattern And set the status to Orphan. Signed-off-by: Joe Perches Cc: Jeroen Vreeken Cc: Hans Verkuil Acked-by: Mauro Carvalho Chehab Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 8b97e46f8b0b..57ef3c10ed5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6465,12 +6465,11 @@ S: Maintained F: drivers/net/usb/rtl8150.c USB SE401 DRIVER -M: Jeroen Vreeken L: linux-usb@vger.kernel.org W: http://www.chello.nl/~j.vreeken/se401/ -S: Maintained +S: Orphan F: Documentation/video4linux/se401.txt -F: drivers/media/video/se401.* +F: drivers/staging/se401/ USB SERIAL BELKIN F5U103 DRIVER M: William Greathouse -- cgit v1.2.3 From 116ab806e871083d5695c25f13f85b0fded71737 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 22 Mar 2011 16:34:38 -0700 Subject: MAINTAINERS: update WINBOND CIR pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5b2e303f6df ("[media] rc-core: convert winbond-cir") moved the files, update the pattern. Signed-off-by: Joe Perches Acked-by: David Härdeman Acked-by: Mauro Carvalho Chehab Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 57ef3c10ed5d..fbf38d791499 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6819,7 +6819,7 @@ F: drivers/scsi/wd7000.c WINBOND CIR DRIVER M: David Härdeman S: Maintained -F: drivers/input/misc/winbond-cir.c +F: drivers/media/rc/winbond-cir.c WIMAX STACK M: Inaky Perez-Gonzalez -- cgit v1.2.3 From 8a5700cd6754a3c88d2ea2f1d7a56f671987fc25 Mon Sep 17 00:00:00 2001 From: Kenneth Heitke Date: Tue, 22 Mar 2011 16:34:39 -0700 Subject: MAINTAINERS: add drivers/platform/msm to MSM subsystem Signed-off-by: Kenneth Heitke Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index fbf38d791499..e11953dc8fa3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -917,6 +917,7 @@ F: drivers/mmc/host/msm_sdcc.c F: drivers/mmc/host/msm_sdcc.h F: drivers/tty/serial/msm_serial.h F: drivers/tty/serial/msm_serial.c +F: drivers/platform/msm/ T: git git://codeaurora.org/quic/kernel/davidb/linux-msm.git S: Maintained -- cgit v1.2.3 From 9263412501022fecef844907129ee2513b5a89de Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 25 Mar 2011 01:56:59 -0700 Subject: memstick: add driver for Ricoh R5C592 card reader Signed-off-by: Maxim Levitsky Acked-by: Alex Dubov Cc: FUJITA Tomonori Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 5 + drivers/memstick/host/Kconfig | 12 + drivers/memstick/host/Makefile | 1 + drivers/memstick/host/r592.c | 908 +++++++++++++++++++++++++++++++++++++++++ drivers/memstick/host/r592.h | 175 ++++++++ 5 files changed, 1101 insertions(+) create mode 100644 drivers/memstick/host/r592.c create mode 100644 drivers/memstick/host/r592.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 749f9cd38089..4fb9017b4413 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5291,6 +5291,11 @@ S: Maintained F: drivers/mtd/nand/r852.c F: drivers/mtd/nand/r852.h +RICOH R5C592 MEMORYSTICK DRIVER +M: Maxim Levitsky +S: Maintained +F: drivers/memstick/host/r592.* + RISCOM8 DRIVER S: Orphan F: Documentation/serial/riscom8.txt diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index 4ce5c8dffb68..cc0997a05171 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig @@ -30,3 +30,15 @@ config MEMSTICK_JMICRON_38X To compile this driver as a module, choose M here: the module will be called jmb38x_ms. + +config MEMSTICK_R592 + tristate "Ricoh R5C592 MemoryStick interface support (EXPERIMENTAL)" + depends on EXPERIMENTAL && PCI + + help + Say Y here if you want to be able to access MemoryStick cards with + the Ricoh R5C592 MemoryStick card reader (which is part of 5 in one + multifunction reader) + + To compile this driver as a module, choose M here: the module will + be called r592. diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile index a1815e9dd019..31ba8d378e46 100644 --- a/drivers/memstick/host/Makefile +++ b/drivers/memstick/host/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o +obj-$(CONFIG_MEMSTICK_R592) += r592.o diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c new file mode 100644 index 000000000000..767406c95291 --- /dev/null +++ b/drivers/memstick/host/r592.c @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * driver for Ricoh memstick readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "r592.h" + +static int enable_dma = 1; +static int debug; + +static const char *tpc_names[] = { + "MS_TPC_READ_MG_STATUS", + "MS_TPC_READ_LONG_DATA", + "MS_TPC_READ_SHORT_DATA", + "MS_TPC_READ_REG", + "MS_TPC_READ_QUAD_DATA", + "INVALID", + "MS_TPC_GET_INT", + "MS_TPC_SET_RW_REG_ADRS", + "MS_TPC_EX_SET_CMD", + "MS_TPC_WRITE_QUAD_DATA", + "MS_TPC_WRITE_REG", + "MS_TPC_WRITE_SHORT_DATA", + "MS_TPC_WRITE_LONG_DATA", + "MS_TPC_SET_CMD", +}; + +/** + * memstick_debug_get_tpc_name - debug helper that returns string for + * a TPC number + */ +const char *memstick_debug_get_tpc_name(int tpc) +{ + return tpc_names[tpc-1]; +} +EXPORT_SYMBOL(memstick_debug_get_tpc_name); + + +/* Read a register*/ +static inline u32 r592_read_reg(struct r592_device *dev, int address) +{ + u32 value = readl(dev->mmio + address); + dbg_reg("reg #%02d == 0x%08x", address, value); + return value; +} + +/* Write a register */ +static inline void r592_write_reg(struct r592_device *dev, + int address, u32 value) +{ + dbg_reg("reg #%02d <- 0x%08x", address, value); + writel(value, dev->mmio + address); +} + +/* Reads a big endian DWORD register */ +static inline u32 r592_read_reg_raw_be(struct r592_device *dev, int address) +{ + u32 value = __raw_readl(dev->mmio + address); + dbg_reg("reg #%02d == 0x%08x", address, value); + return be32_to_cpu(value); +} + +/* Writes a big endian DWORD register */ +static inline void r592_write_reg_raw_be(struct r592_device *dev, + int address, u32 value) +{ + dbg_reg("reg #%02d <- 0x%08x", address, value); + __raw_writel(cpu_to_be32(value), dev->mmio + address); +} + +/* Set specific bits in a register (little endian) */ +static inline void r592_set_reg_mask(struct r592_device *dev, + int address, u32 mask) +{ + u32 reg = readl(dev->mmio + address); + dbg_reg("reg #%02d |= 0x%08x (old =0x%08x)", address, mask, reg); + writel(reg | mask , dev->mmio + address); +} + +/* Clear specific bits in a register (little endian) */ +static inline void r592_clear_reg_mask(struct r592_device *dev, + int address, u32 mask) +{ + u32 reg = readl(dev->mmio + address); + dbg_reg("reg #%02d &= 0x%08x (old = 0x%08x, mask = 0x%08x)", + address, ~mask, reg, mask); + writel(reg & ~mask, dev->mmio + address); +} + + +/* Wait for status bits while checking for errors */ +static int r592_wait_status(struct r592_device *dev, u32 mask, u32 wanted_mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + u32 reg = r592_read_reg(dev, R592_STATUS); + + if ((reg & mask) == wanted_mask) + return 0; + + while (time_before(jiffies, timeout)) { + + reg = r592_read_reg(dev, R592_STATUS); + + if ((reg & mask) == wanted_mask) + return 0; + + if (reg & (R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR)) + return -EIO; + + cpu_relax(); + } + return -ETIME; +} + + +/* Enable/disable device */ +static int r592_enable_device(struct r592_device *dev, bool enable) +{ + dbg("%sabling the device", enable ? "en" : "dis"); + + if (enable) { + + /* Power up the card */ + r592_write_reg(dev, R592_POWER, R592_POWER_0 | R592_POWER_1); + + /* Perform a reset */ + r592_set_reg_mask(dev, R592_IO, R592_IO_RESET); + + msleep(100); + } else + /* Power down the card */ + r592_write_reg(dev, R592_POWER, 0); + + return 0; +} + +/* Set serial/parallel mode */ +static int r592_set_mode(struct r592_device *dev, bool parallel_mode) +{ + if (!parallel_mode) { + dbg("switching to serial mode"); + + /* Set serial mode */ + r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_SERIAL); + + r592_clear_reg_mask(dev, R592_POWER, R592_POWER_20); + + } else { + dbg("switching to parallel mode"); + + /* This setting should be set _before_ switch TPC */ + r592_set_reg_mask(dev, R592_POWER, R592_POWER_20); + + r592_clear_reg_mask(dev, R592_IO, + R592_IO_SERIAL1 | R592_IO_SERIAL2); + + /* Set the parallel mode now */ + r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_PARALLEL); + } + + dev->parallel_mode = parallel_mode; + return 0; +} + +/* Perform a controller reset without powering down the card */ +static void r592_host_reset(struct r592_device *dev) +{ + r592_set_reg_mask(dev, R592_IO, R592_IO_RESET); + msleep(100); + r592_set_mode(dev, dev->parallel_mode); +} + +/* Disable all hardware interrupts */ +static void r592_clear_interrupts(struct r592_device *dev) +{ + /* Disable & ACK all interrupts */ + r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_ACK_MASK); + r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_EN_MASK); +} + +/* Tests if there is an CRC error */ +static int r592_test_io_error(struct r592_device *dev) +{ + if (!(r592_read_reg(dev, R592_STATUS) & + (R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR))) + return 0; + + return -EIO; +} + +/* Ensure that FIFO is ready for use */ +static int r592_test_fifo_empty(struct r592_device *dev) +{ + if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY) + return 0; + + dbg("FIFO not ready, trying to reset the device"); + r592_host_reset(dev); + + if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY) + return 0; + + message("FIFO still not ready, giving up"); + return -EIO; +} + +/* Activates the DMA transfer from to FIFO */ +static void r592_start_dma(struct r592_device *dev, bool is_write) +{ + unsigned long flags; + u32 reg; + spin_lock_irqsave(&dev->irq_lock, flags); + + /* Ack interrupts (just in case) + enable them */ + r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK); + r592_set_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK); + + /* Set DMA address */ + r592_write_reg(dev, R592_FIFO_DMA, sg_dma_address(&dev->req->sg)); + + /* Enable the DMA */ + reg = r592_read_reg(dev, R592_FIFO_DMA_SETTINGS); + reg |= R592_FIFO_DMA_SETTINGS_EN; + + if (!is_write) + reg |= R592_FIFO_DMA_SETTINGS_DIR; + else + reg &= ~R592_FIFO_DMA_SETTINGS_DIR; + r592_write_reg(dev, R592_FIFO_DMA_SETTINGS, reg); + + spin_unlock_irqrestore(&dev->irq_lock, flags); +} + +/* Cleanups DMA related settings */ +static void r592_stop_dma(struct r592_device *dev, int error) +{ + r592_clear_reg_mask(dev, R592_FIFO_DMA_SETTINGS, + R592_FIFO_DMA_SETTINGS_EN); + + /* This is only a precation */ + r592_write_reg(dev, R592_FIFO_DMA, + dev->dummy_dma_page_physical_address); + + r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK); + r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK); + dev->dma_error = error; +} + +/* Test if hardware supports DMA */ +static void r592_check_dma(struct r592_device *dev) +{ + dev->dma_capable = enable_dma && + (r592_read_reg(dev, R592_FIFO_DMA_SETTINGS) & + R592_FIFO_DMA_SETTINGS_CAP); +} + +/* Transfers fifo contents in/out using DMA */ +static int r592_transfer_fifo_dma(struct r592_device *dev) +{ + int len, sg_count; + bool is_write; + + if (!dev->dma_capable || !dev->req->long_data) + return -EINVAL; + + len = dev->req->sg.length; + is_write = dev->req->data_dir == WRITE; + + if (len != R592_LFIFO_SIZE) + return -EINVAL; + + dbg_verbose("doing dma transfer"); + + dev->dma_error = 0; + INIT_COMPLETION(dev->dma_done); + + /* TODO: hidden assumption about nenth beeing always 1 */ + sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? + PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + if (sg_count != 1 || + (sg_dma_len(&dev->req->sg) < dev->req->sg.length)) { + message("problem in dma_map_sg"); + return -EIO; + } + + r592_start_dma(dev, is_write); + + /* Wait for DMA completion */ + if (!wait_for_completion_timeout( + &dev->dma_done, msecs_to_jiffies(1000))) { + message("DMA timeout"); + r592_stop_dma(dev, -ETIMEDOUT); + } + + dma_unmap_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? + PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + + return dev->dma_error; +} + +/* + * Writes the FIFO in 4 byte chunks. + * If length isn't 4 byte aligned, rest of the data if put to a fifo + * to be written later + * Use r592_flush_fifo_write to flush that fifo when writing for the + * last time + */ +static void r592_write_fifo_pio(struct r592_device *dev, + unsigned char *buffer, int len) +{ + /* flush spill from former write */ + if (!kfifo_is_empty(&dev->pio_fifo)) { + + u8 tmp[4] = {0}; + int copy_len = kfifo_in(&dev->pio_fifo, buffer, len); + + if (!kfifo_is_full(&dev->pio_fifo)) + return; + len -= copy_len; + buffer += copy_len; + + copy_len = kfifo_out(&dev->pio_fifo, tmp, 4); + WARN_ON(copy_len != 4); + r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)tmp); + } + + WARN_ON(!kfifo_is_empty(&dev->pio_fifo)); + + /* write full dwords */ + while (len >= 4) { + r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer); + buffer += 4; + len -= 4; + } + + /* put remaining bytes to the spill */ + if (len) + kfifo_in(&dev->pio_fifo, buffer, len); +} + +/* Flushes the temporary FIFO used to make aligned DWORD writes */ +static void r592_flush_fifo_write(struct r592_device *dev) +{ + u8 buffer[4] = { 0 }; + int len; + + if (kfifo_is_empty(&dev->pio_fifo)) + return; + + len = kfifo_out(&dev->pio_fifo, buffer, 4); + r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer); +} + +/* + * Read a fifo in 4 bytes chunks. + * If input doesn't fit the buffer, it places bytes of last dword in spill + * buffer, so that they don't get lost on last read, just throw these away. + */ +static void r592_read_fifo_pio(struct r592_device *dev, + unsigned char *buffer, int len) +{ + u8 tmp[4]; + + /* Read from last spill */ + if (!kfifo_is_empty(&dev->pio_fifo)) { + int bytes_copied = + kfifo_out(&dev->pio_fifo, buffer, min(4, len)); + buffer += bytes_copied; + len -= bytes_copied; + + if (!kfifo_is_empty(&dev->pio_fifo)) + return; + } + + /* Reads dwords from FIFO */ + while (len >= 4) { + *(u32 *)buffer = r592_read_reg_raw_be(dev, R592_FIFO_PIO); + buffer += 4; + len -= 4; + } + + if (len) { + *(u32 *)tmp = r592_read_reg_raw_be(dev, R592_FIFO_PIO); + kfifo_in(&dev->pio_fifo, tmp, 4); + len -= kfifo_out(&dev->pio_fifo, buffer, len); + } + + WARN_ON(len); + return; +} + +/* Transfers actual data using PIO. */ +static int r592_transfer_fifo_pio(struct r592_device *dev) +{ + unsigned long flags; + + bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS; + struct sg_mapping_iter miter; + + kfifo_reset(&dev->pio_fifo); + + if (!dev->req->long_data) { + if (is_write) { + r592_write_fifo_pio(dev, dev->req->data, + dev->req->data_len); + r592_flush_fifo_write(dev); + } else + r592_read_fifo_pio(dev, dev->req->data, + dev->req->data_len); + return 0; + } + + local_irq_save(flags); + sg_miter_start(&miter, &dev->req->sg, 1, SG_MITER_ATOMIC | + (is_write ? SG_MITER_FROM_SG : SG_MITER_TO_SG)); + + /* Do the transfer fifo<->memory*/ + while (sg_miter_next(&miter)) + if (is_write) + r592_write_fifo_pio(dev, miter.addr, miter.length); + else + r592_read_fifo_pio(dev, miter.addr, miter.length); + + + /* Write last few non aligned bytes*/ + if (is_write) + r592_flush_fifo_write(dev); + + sg_miter_stop(&miter); + local_irq_restore(flags); + return 0; +} + +/* Executes one TPC (data is read/written from small or large fifo) */ +static void r592_execute_tpc(struct r592_device *dev) +{ + bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS; + int len, error; + u32 status, reg; + + if (!dev->req) { + message("BUG: tpc execution without request!"); + return; + } + + len = dev->req->long_data ? + dev->req->sg.length : dev->req->data_len; + + /* Ensure that FIFO can hold the input data */ + if (len > R592_LFIFO_SIZE) { + message("IO: hardware doesn't support TPCs longer that 512"); + error = -ENOSYS; + goto out; + } + + if (!(r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_PRSNT)) { + dbg("IO: refusing to send TPC because card is absent"); + error = -ENODEV; + goto out; + } + + dbg("IO: executing %s LEN=%d", + memstick_debug_get_tpc_name(dev->req->tpc), len); + + /* Set IO direction */ + if (is_write) + r592_set_reg_mask(dev, R592_IO, R592_IO_DIRECTION); + else + r592_clear_reg_mask(dev, R592_IO, R592_IO_DIRECTION); + + + error = r592_test_fifo_empty(dev); + if (error) + goto out; + + /* Transfer write data */ + if (is_write) { + error = r592_transfer_fifo_dma(dev); + if (error == -EINVAL) + error = r592_transfer_fifo_pio(dev); + } + + if (error) + goto out; + + /* Trigger the TPC */ + reg = (len << R592_TPC_EXEC_LEN_SHIFT) | + (dev->req->tpc << R592_TPC_EXEC_TPC_SHIFT) | + R592_TPC_EXEC_BIG_FIFO; + + r592_write_reg(dev, R592_TPC_EXEC, reg); + + /* Wait for TPC completion */ + status = R592_STATUS_RDY; + if (dev->req->need_card_int) + status |= R592_STATUS_CED; + + error = r592_wait_status(dev, status, status); + if (error) { + message("card didn't respond"); + goto out; + } + + /* Test IO errors */ + error = r592_test_io_error(dev); + if (error) { + dbg("IO error"); + goto out; + } + + /* Read data from FIFO */ + if (!is_write) { + error = r592_transfer_fifo_dma(dev); + if (error == -EINVAL) + error = r592_transfer_fifo_pio(dev); + } + + /* read INT reg. This can be shortened with shifts, but that way + its more readable */ + if (dev->parallel_mode && dev->req->need_card_int) { + + dev->req->int_reg = 0; + status = r592_read_reg(dev, R592_STATUS); + + if (status & R592_STATUS_P_CMDNACK) + dev->req->int_reg |= MEMSTICK_INT_CMDNAK; + if (status & R592_STATUS_P_BREQ) + dev->req->int_reg |= MEMSTICK_INT_BREQ; + if (status & R592_STATUS_P_INTERR) + dev->req->int_reg |= MEMSTICK_INT_ERR; + if (status & R592_STATUS_P_CED) + dev->req->int_reg |= MEMSTICK_INT_CED; + } + + if (error) + dbg("FIFO read error"); +out: + dev->req->error = error; + r592_clear_reg_mask(dev, R592_REG_MSC, R592_REG_MSC_LED); + return; +} + +/* Main request processing thread */ +static int r592_process_thread(void *data) +{ + int error; + struct r592_device *dev = (struct r592_device *)data; + unsigned long flags; + + while (!kthread_should_stop()) { + spin_lock_irqsave(&dev->io_thread_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + error = memstick_next_req(dev->host, &dev->req); + spin_unlock_irqrestore(&dev->io_thread_lock, flags); + + if (error) { + if (error == -ENXIO || error == -EAGAIN) { + dbg_verbose("IO: done IO, sleeping"); + } else { + dbg("IO: unknown error from " + "memstick_next_req %d", error); + } + + if (kthread_should_stop()) + set_current_state(TASK_RUNNING); + + schedule(); + } else { + set_current_state(TASK_RUNNING); + r592_execute_tpc(dev); + } + } + return 0; +} + +/* Reprogram chip to detect change in card state */ +/* eg, if card is detected, arm it to detect removal, and vice versa */ +static void r592_update_card_detect(struct r592_device *dev) +{ + u32 reg = r592_read_reg(dev, R592_REG_MSC); + bool card_detected = reg & R592_REG_MSC_PRSNT; + + dbg("update card detect. card state: %s", card_detected ? + "present" : "absent"); + + reg &= ~((R592_REG_MSC_IRQ_REMOVE | R592_REG_MSC_IRQ_INSERT) << 16); + + if (card_detected) + reg |= (R592_REG_MSC_IRQ_REMOVE << 16); + else + reg |= (R592_REG_MSC_IRQ_INSERT << 16); + + r592_write_reg(dev, R592_REG_MSC, reg); +} + +/* Timer routine that fires 1 second after last card detection event, */ +static void r592_detect_timer(long unsigned int data) +{ + struct r592_device *dev = (struct r592_device *)data; + r592_update_card_detect(dev); + memstick_detect_change(dev->host); +} + +/* Interrupt handler */ +static irqreturn_t r592_irq(int irq, void *data) +{ + struct r592_device *dev = (struct r592_device *)data; + irqreturn_t ret = IRQ_NONE; + u32 reg; + u16 irq_enable, irq_status; + unsigned long flags; + int error; + + spin_lock_irqsave(&dev->irq_lock, flags); + + reg = r592_read_reg(dev, R592_REG_MSC); + irq_enable = reg >> 16; + irq_status = reg & 0xFFFF; + + /* Ack the interrupts */ + reg &= ~irq_status; + r592_write_reg(dev, R592_REG_MSC, reg); + + /* Get the IRQ status minus bits that aren't enabled */ + irq_status &= (irq_enable); + + /* Due to limitation of memstick core, we don't look at bits that + indicate that card was removed/inserted and/or present */ + if (irq_status & (R592_REG_MSC_IRQ_INSERT | R592_REG_MSC_IRQ_REMOVE)) { + + bool card_was_added = irq_status & R592_REG_MSC_IRQ_INSERT; + ret = IRQ_HANDLED; + + message("IRQ: card %s", card_was_added ? "added" : "removed"); + + mod_timer(&dev->detect_timer, + jiffies + msecs_to_jiffies(card_was_added ? 500 : 50)); + } + + if (irq_status & + (R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR)) { + ret = IRQ_HANDLED; + + if (irq_status & R592_REG_MSC_FIFO_DMA_ERR) { + message("IRQ: DMA error"); + error = -EIO; + } else { + dbg_verbose("IRQ: dma done"); + error = 0; + } + + r592_stop_dma(dev, error); + complete(&dev->dma_done); + } + + spin_unlock_irqrestore(&dev->irq_lock, flags); + return ret; +} + +/* External inteface: set settings */ +static int r592_set_param(struct memstick_host *host, + enum memstick_param param, int value) +{ + struct r592_device *dev = memstick_priv(host); + + switch (param) { + case MEMSTICK_POWER: + switch (value) { + case MEMSTICK_POWER_ON: + return r592_enable_device(dev, true); + case MEMSTICK_POWER_OFF: + return r592_enable_device(dev, false); + default: + return -EINVAL; + } + case MEMSTICK_INTERFACE: + switch (value) { + case MEMSTICK_SERIAL: + return r592_set_mode(dev, 0); + case MEMSTICK_PAR4: + return r592_set_mode(dev, 1); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +/* External interface: submit requests */ +static void r592_submit_req(struct memstick_host *host) +{ + struct r592_device *dev = memstick_priv(host); + unsigned long flags; + + if (dev->req) + return; + + spin_lock_irqsave(&dev->io_thread_lock, flags); + if (wake_up_process(dev->io_thread)) + dbg_verbose("IO thread woken to process requests"); + spin_unlock_irqrestore(&dev->io_thread_lock, flags); +} + +static const struct pci_device_id r592_pci_id_tbl[] = { + + { PCI_VDEVICE(RICOH, 0x0592), }, + { }, +}; + +/* Main entry */ +static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int error = -ENOMEM; + struct memstick_host *host; + struct r592_device *dev; + + /* Allocate memory */ + host = memstick_alloc_host(sizeof(struct r592_device), &pdev->dev); + if (!host) + goto error1; + + dev = memstick_priv(host); + dev->host = host; + dev->pci_dev = pdev; + pci_set_drvdata(pdev, dev); + + /* pci initialization */ + error = pci_enable_device(pdev); + if (error) + goto error2; + + pci_set_master(pdev); + error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (error) + goto error3; + + error = pci_request_regions(pdev, DRV_NAME); + if (error) + goto error3; + + dev->mmio = pci_ioremap_bar(pdev, 0); + if (!dev->mmio) + goto error4; + + dev->irq = pdev->irq; + spin_lock_init(&dev->irq_lock); + spin_lock_init(&dev->io_thread_lock); + init_completion(&dev->dma_done); + INIT_KFIFO(dev->pio_fifo); + setup_timer(&dev->detect_timer, + r592_detect_timer, (long unsigned int)dev); + + /* Host initialization */ + host->caps = MEMSTICK_CAP_PAR4; + host->request = r592_submit_req; + host->set_param = r592_set_param; + r592_check_dma(dev); + + dev->io_thread = kthread_run(r592_process_thread, dev, "r592_io"); + if (IS_ERR(dev->io_thread)) { + error = PTR_ERR(dev->io_thread); + goto error5; + } + + /* This is just a precation, so don't fail */ + dev->dummy_dma_page = pci_alloc_consistent(pdev, PAGE_SIZE, + &dev->dummy_dma_page_physical_address); + r592_stop_dma(dev , 0); + + if (request_irq(dev->irq, &r592_irq, IRQF_SHARED, + DRV_NAME, dev)) + goto error6; + + r592_update_card_detect(dev); + if (memstick_add_host(host)) + goto error7; + + message("driver succesfully loaded"); + return 0; +error7: + free_irq(dev->irq, dev); +error6: + if (dev->dummy_dma_page) + pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page, + dev->dummy_dma_page_physical_address); + + kthread_stop(dev->io_thread); +error5: + iounmap(dev->mmio); +error4: + pci_release_regions(pdev); +error3: + pci_disable_device(pdev); +error2: + memstick_free_host(host); +error1: + return error; +} + +static void r592_remove(struct pci_dev *pdev) +{ + int error = 0; + struct r592_device *dev = pci_get_drvdata(pdev); + + /* Stop the processing thread. + That ensures that we won't take any more requests */ + kthread_stop(dev->io_thread); + + r592_enable_device(dev, false); + + while (!error && dev->req) { + dev->req->error = -ETIME; + error = memstick_next_req(dev->host, &dev->req); + } + memstick_remove_host(dev->host); + + free_irq(dev->irq, dev); + iounmap(dev->mmio); + pci_release_regions(pdev); + pci_disable_device(pdev); + memstick_free_host(dev->host); + + if (dev->dummy_dma_page) + pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page, + dev->dummy_dma_page_physical_address); +} + +#ifdef CONFIG_PM +static int r592_suspend(struct device *core_dev) +{ + struct pci_dev *pdev = to_pci_dev(core_dev); + struct r592_device *dev = pci_get_drvdata(pdev); + + r592_clear_interrupts(dev); + memstick_suspend_host(dev->host); + del_timer_sync(&dev->detect_timer); + return 0; +} + +static int r592_resume(struct device *core_dev) +{ + struct pci_dev *pdev = to_pci_dev(core_dev); + struct r592_device *dev = pci_get_drvdata(pdev); + + r592_clear_interrupts(dev); + r592_enable_device(dev, false); + memstick_resume_host(dev->host); + r592_update_card_detect(dev); + return 0; +} + +SIMPLE_DEV_PM_OPS(r592_pm_ops, r592_suspend, r592_resume); +#endif + +MODULE_DEVICE_TABLE(pci, r592_pci_id_tbl); + +static struct pci_driver r852_pci_driver = { + .name = DRV_NAME, + .id_table = r592_pci_id_tbl, + .probe = r592_probe, + .remove = r592_remove, +#ifdef CONFIG_PM + .driver.pm = &r592_pm_ops, +#endif +}; + +static __init int r592_module_init(void) +{ + return pci_register_driver(&r852_pci_driver); +} + +static void __exit r592_module_exit(void) +{ + pci_unregister_driver(&r852_pci_driver); +} + +module_init(r592_module_init); +module_exit(r592_module_exit); + +module_param(enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-3)"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky "); +MODULE_DESCRIPTION("Ricoh R5C592 Memstick/Memstick PRO card reader driver"); diff --git a/drivers/memstick/host/r592.h b/drivers/memstick/host/r592.h new file mode 100644 index 000000000000..eee264e6028f --- /dev/null +++ b/drivers/memstick/host/r592.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * driver for Ricoh memstick readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef R592_H + +#include +#include +#include +#include +#include +#include + +/* write to this reg (number,len) triggers TPC execution */ +#define R592_TPC_EXEC 0x00 +#define R592_TPC_EXEC_LEN_SHIFT 16 /* Bits 16..25 are TPC len */ +#define R592_TPC_EXEC_BIG_FIFO (1 << 26) /* If bit 26 is set, large fifo is used (reg 48) */ +#define R592_TPC_EXEC_TPC_SHIFT 28 /* Bits 28..31 are the TPC number */ + + +/* Window for small TPC fifo (big endian)*/ +/* reads and writes always are done in 8 byte chunks */ +/* Not used in driver, because large fifo does better job */ +#define R592_SFIFO 0x08 + + +/* Status register (ms int, small fifo, IO)*/ +#define R592_STATUS 0x10 + /* Parallel INT bits */ +#define R592_STATUS_P_CMDNACK (1 << 16) /* INT reg: NACK (parallel mode) */ +#define R592_STATUS_P_BREQ (1 << 17) /* INT reg: card ready (parallel mode)*/ +#define R592_STATUS_P_INTERR (1 << 18) /* INT reg: int error (parallel mode)*/ +#define R592_STATUS_P_CED (1 << 19) /* INT reg: command done (parallel mode) */ + + /* Fifo status */ +#define R592_STATUS_SFIFO_FULL (1 << 20) /* Small Fifo almost full (last chunk is written) */ +#define R592_STATUS_SFIFO_EMPTY (1 << 21) /* Small Fifo empty */ + + /* Error detection via CRC */ +#define R592_STATUS_SEND_ERR (1 << 24) /* Send failed */ +#define R592_STATUS_RECV_ERR (1 << 25) /* Recieve failed */ + + /* Card state */ +#define R592_STATUS_RDY (1 << 28) /* RDY signal recieved */ +#define R592_STATUS_CED (1 << 29) /* INT: Command done (serial mode)*/ +#define R592_STATUS_SFIFO_INPUT (1 << 30) /* Small fifo recieved data*/ + +#define R592_SFIFO_SIZE 32 /* total size of small fifo is 32 bytes */ +#define R592_SFIFO_PACKET 8 /* packet size of small fifo */ + +/* IO control */ +#define R592_IO 0x18 +#define R592_IO_16 (1 << 16) /* Set by default, can be cleared */ +#define R592_IO_18 (1 << 18) /* Set by default, can be cleared */ +#define R592_IO_SERIAL1 (1 << 20) /* Set by default, can be cleared, (cleared on parallel) */ +#define R592_IO_22 (1 << 22) /* Set by default, can be cleared */ +#define R592_IO_DIRECTION (1 << 24) /* TPC direction (1 write 0 read) */ +#define R592_IO_26 (1 << 26) /* Set by default, can be cleared */ +#define R592_IO_SERIAL2 (1 << 30) /* Set by default, can be cleared (cleared on parallel), serial doesn't work if unset */ +#define R592_IO_RESET (1 << 31) /* Reset, sets defaults*/ + + +/* Turns hardware on/off */ +#define R592_POWER 0x20 /* bits 0-7 writeable */ +#define R592_POWER_0 (1 << 0) /* set on start, cleared on stop - must be set*/ +#define R592_POWER_1 (1 << 1) /* set on start, cleared on stop - must be set*/ +#define R592_POWER_3 (1 << 3) /* must be clear */ +#define R592_POWER_20 (1 << 5) /* set before switch to parallel */ + +/* IO mode*/ +#define R592_IO_MODE 0x24 +#define R592_IO_MODE_SERIAL 1 +#define R592_IO_MODE_PARALLEL 3 + + +/* IRQ,card detection,large fifo (first word irq status, second enable) */ +/* IRQs are ACKed by clearing the bits */ +#define R592_REG_MSC 0x28 +#define R592_REG_MSC_PRSNT (1 << 1) /* card present (only status)*/ +#define R592_REG_MSC_IRQ_INSERT (1 << 8) /* detect insert / card insered */ +#define R592_REG_MSC_IRQ_REMOVE (1 << 9) /* detect removal / card removed */ +#define R592_REG_MSC_FIFO_EMPTY (1 << 10) /* fifo is empty */ +#define R592_REG_MSC_FIFO_DMA_DONE (1 << 11) /* dma enable / dma done */ + +#define R592_REG_MSC_FIFO_USER_ORN (1 << 12) /* set if software reads empty fifo (if R592_REG_MSC_FIFO_EMPTY is set) */ +#define R592_REG_MSC_FIFO_MISMATH (1 << 13) /* set if amount of data in fifo doesn't match amount in TPC */ +#define R592_REG_MSC_FIFO_DMA_ERR (1 << 14) /* IO failure */ +#define R592_REG_MSC_LED (1 << 15) /* clear to turn led off (only status)*/ + +#define DMA_IRQ_ACK_MASK \ + (R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR) + +#define DMA_IRQ_EN_MASK (DMA_IRQ_ACK_MASK << 16) + +#define IRQ_ALL_ACK_MASK 0x00007F00 +#define IRQ_ALL_EN_MASK (IRQ_ALL_ACK_MASK << 16) + +/* DMA address for large FIFO read/writes*/ +#define R592_FIFO_DMA 0x2C + +/* PIO access to large FIFO (512 bytes) (big endian)*/ +#define R592_FIFO_PIO 0x30 +#define R592_LFIFO_SIZE 512 /* large fifo size */ + + +/* large FIFO DMA settings */ +#define R592_FIFO_DMA_SETTINGS 0x34 +#define R592_FIFO_DMA_SETTINGS_EN (1 << 0) /* DMA enabled */ +#define R592_FIFO_DMA_SETTINGS_DIR (1 << 1) /* Dma direction (1 read, 0 write) */ +#define R592_FIFO_DMA_SETTINGS_CAP (1 << 24) /* Dma is aviable */ + +/* Maybe just an delay */ +/* Bits 17..19 are just number */ +/* bit 16 is set, then bit 20 is waited */ +/* time to wait is about 50 spins * 2 ^ (bits 17..19) */ +/* seems to be possible just to ignore */ +/* Probably debug register */ +#define R592_REG38 0x38 +#define R592_REG38_CHANGE (1 << 16) /* Start bit */ +#define R592_REG38_DONE (1 << 20) /* HW set this after the delay */ +#define R592_REG38_SHIFT 17 + +/* Debug register, written (0xABCDEF00) when error happens - not used*/ +#define R592_REG_3C 0x3C + +struct r592_device { + struct pci_dev *pci_dev; + struct memstick_host *host; /* host backpointer */ + struct memstick_request *req; /* current request */ + + /* Registers, IRQ */ + void __iomem *mmio; + int irq; + spinlock_t irq_lock; + spinlock_t io_thread_lock; + struct timer_list detect_timer; + + struct task_struct *io_thread; + bool parallel_mode; + + DECLARE_KFIFO(pio_fifo, u8, sizeof(u32)); + + /* DMA area */ + int dma_capable; + int dma_error; + struct completion dma_done; + void *dummy_dma_page; + dma_addr_t dummy_dma_page_physical_address; + +}; + +#define DRV_NAME "r592" + + +#define message(format, ...) \ + printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) + +#define __dbg(level, format, ...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG DRV_NAME \ + ": " format "\n", ## __VA_ARGS__); \ + } while (0) + + +#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__) +#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__) +#define dbg_reg(format, ...) __dbg(3, format, ## __VA_ARGS__) + +#endif -- cgit v1.2.3 From b15194b7076d6bb7b0b6f3efcb6a103e6cbcc5db Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sat, 26 Mar 2011 20:48:57 +0100 Subject: um: Add myself as co-maintainer Signed-off-by: Richard Weinberger Acked-and-appreciated-by: Thomas Gleixner Signed-off-by: Linus Torvalds --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4fb9017b4413..42471b830967 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6633,6 +6633,7 @@ F: drivers/media/video/zr364xx.c USER-MODE LINUX (UML) M: Jeff Dike +M: Richard Weinberger L: user-mode-linux-devel@lists.sourceforge.net L: user-mode-linux-user@lists.sourceforge.net W: http://user-mode-linux.sourceforge.net -- cgit v1.2.3 From 04a6553f0766df3f56830c89b7da2f618b7ef0b0 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Sat, 26 Mar 2011 17:22:04 +1100 Subject: apm: orphan the driver I no longer have access to any hardware that uses APM and have not provided real maintenance for several years. Hopefully someone with the hardware or energy will step forward if the driver is to be kept. Signed-off-by: Stephen Rothwell Signed-off-by: Linus Torvalds --- MAINTAINERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 42471b830967..8aa1cacddbcc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -548,10 +548,8 @@ S: Maintained F: sound/aoa/ APM DRIVER -M: Stephen Rothwell L: linux-laptop@vger.kernel.org -W: http://www.canb.auug.org.au/~sfr/ -S: Supported +S: Orphan F: arch/x86/kernel/apm_32.c F: include/linux/apm_bios.h -- cgit v1.2.3 From b229ece9911cc0b2415d3ffd55d7b2a28d30b614 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:40 +0100 Subject: asus-nb-wmi: Asus Notebooks WMI Driver Introduce a new driver for Asus Notebooks shipped with a WMI device instead of the old ACPI device. The WMI device is almost the same as the one present in Eee PC, but the event guid and the keymap are different. The keymap comes from asus-laptop module. On Asus notebooks, when you call the WMI device, you always need a 64bit buffer, even if you only want to get the state of a device (tested on a G73). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- MAINTAINERS | 30 ++---------- drivers/platform/x86/Kconfig | 16 ++++++- drivers/platform/x86/Makefile | 1 + drivers/platform/x86/asus-nb-wmi.c | 98 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 28 deletions(-) create mode 100644 drivers/platform/x86/asus-nb-wmi.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 8aa1cacddbcc..6b4b9cdec370 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1157,14 +1157,14 @@ S: Maintained F: Documentation/hwmon/asc7621 F: drivers/hwmon/asc7621.c -ASUS ACPI EXTRAS DRIVER +ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS M: Corentin Chary -M: Karol Kozimor L: acpi4asus-user@lists.sourceforge.net L: platform-driver-x86@vger.kernel.org W: http://acpi4asus.sf.net S: Maintained -F: drivers/platform/x86/asus_acpi.c +F: drivers/platform/x86/asus*.c +F: drivers/platform/x86/eeepc*.c ASUS ASB100 HARDWARE MONITOR DRIVER M: "Mark M. Hoffman" @@ -1172,14 +1172,6 @@ L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/asb100.c -ASUS LAPTOP EXTRAS DRIVER -M: Corentin Chary -L: acpi4asus-user@lists.sourceforge.net -L: platform-driver-x86@vger.kernel.org -W: http://acpi4asus.sf.net -S: Maintained -F: drivers/platform/x86/asus-laptop.c - ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API M: Dan Williams W: http://sourceforge.net/projects/xscaleiop @@ -2414,22 +2406,6 @@ T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/usb/misc/ua101.c -EEEPC LAPTOP EXTRAS DRIVER -M: Corentin Chary -L: acpi4asus-user@lists.sourceforge.net -L: platform-driver-x86@vger.kernel.org -W: http://acpi4asus.sf.net -S: Maintained -F: drivers/platform/x86/eeepc-laptop.c - -EEEPC WMI EXTRAS DRIVER -M: Corentin Chary -L: acpi4asus-user@lists.sourceforge.net -L: platform-driver-x86@vger.kernel.org -W: http://acpi4asus.sf.net -S: Maintained -F: drivers/platform/x86/eeepc-wmi.c - EFIFB FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org M: Peter Jones diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7fde7ba01ab0..45f4d63c7939 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -465,11 +465,25 @@ config ASUS_WMI select LEDS_CLASS select NEW_LEDS ---help--- - Say Y here if you have a WMI aware Asus laptop (like Eee PCs). + Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new + Asus Notebooks). To compile this driver as a module, choose M here: the module will be called asus-wmi. +config ASUS_NB_WMI + tristate "Asus Notebook WMI Driver (EXPERIMENTAL)" + depends on ASUS_WMI + ---help--- + This is a driver for newer Asus notebooks. It adds extra features + like wireless radio and bluetooth control, leds, hotkeys, backlight... + + For more informations, see + + + If you have an ACPI-WMI compatible Asus Notebook, say Y or M + here. + config EEEPC_WMI tristate "Eee PC WMI Driver (EXPERIMENTAL)" depends on ASUS_WMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index f9c83f48c922..0f7b23c0714b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o obj-$(CONFIG_ASUS_WMI) += asus-wmi.o +obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c new file mode 100644 index 000000000000..0580d99b0798 --- /dev/null +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -0,0 +1,98 @@ +/* + * Asus Notebooks WMI hotkey driver + * + * Copyright(C) 2010 Corentin Chary + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "asus-wmi.h" + +#define ASUS_NB_WMI_FILE "asus-nb-wmi" + +MODULE_AUTHOR("Corentin Chary "); +MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver"); +MODULE_LICENSE("GPL"); + +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" + +MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID); + +static const struct key_entry asus_nb_wmi_keymap[] = { + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x32, { KEY_MUTE } }, + { KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */ + { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */ + { KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, + { KE_KEY, 0x41, { KEY_NEXTSONG } }, + { KE_KEY, 0x43, { KEY_STOPCD } }, + { KE_KEY, 0x45, { KEY_PLAYPAUSE } }, + { KE_KEY, 0x4c, { KEY_MEDIA } }, + { KE_KEY, 0x50, { KEY_EMAIL } }, + { KE_KEY, 0x51, { KEY_WWW } }, + { KE_KEY, 0x55, { KEY_CALC } }, + { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ + { KE_KEY, 0x5D, { KEY_WLAN } }, + { KE_KEY, 0x5E, { KEY_WLAN } }, + { KE_KEY, 0x5F, { KEY_WLAN } }, + { KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, + { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, + { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, + { KE_KEY, 0x82, { KEY_CAMERA } }, + { KE_KEY, 0x88, { KEY_RFKILL } }, + { KE_KEY, 0x8A, { KEY_PROG1 } }, + { KE_KEY, 0x95, { KEY_MEDIA } }, + { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0xb5, { KEY_CALC } }, + { KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, + { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, + { KE_END, 0}, +}; + +static struct asus_wmi_driver asus_nb_wmi_driver = { + .name = ASUS_NB_WMI_FILE, + .owner = THIS_MODULE, + .event_guid = ASUS_NB_WMI_EVENT_GUID, + .keymap = asus_nb_wmi_keymap, + .input_name = "Asus WMI hotkeys", + .input_phys = ASUS_NB_WMI_FILE "/input0", +}; + + +static int __init asus_nb_wmi_init(void) +{ + return asus_wmi_register_driver(&asus_nb_wmi_driver); +} + +static void __exit asus_nb_wmi_exit(void) +{ + asus_wmi_unregister_driver(&asus_nb_wmi_driver); +} + +module_init(asus_nb_wmi_init); +module_exit(asus_nb_wmi_exit); -- cgit v1.2.3 From 9b57e1a79e2142df8412ab223c90ba6f47c6b3a3 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Sun, 3 Apr 2011 23:12:23 +0000 Subject: MAINTAINERS: add entry for Xen network backend Signed-off-by: Ian Campbell Signed-off-by: David S. Miller --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6b4b9cdec370..649600cb8ec9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6916,6 +6916,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86. S: Maintained F: drivers/platform/x86 +XEN NETWORK BACKEND DRIVER +M: Ian Campbell +L: xen-devel@lists.xensource.com (moderated for non-subscribers) +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/xen-netback/* + XEN PCI SUBSYSTEM M: Konrad Rzeszutek Wilk L: xen-devel@lists.xensource.com (moderated for non-subscribers) -- cgit v1.2.3 From b9cbfcbe260c43bac3a6d941b095cc4dcf70e614 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Fri, 8 Apr 2011 11:29:42 +0530 Subject: MAINTAINERS: Update Samsung ASoC maintainer's id Signed-off-by: Jassi Brar Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6b4b9cdec370..c4fb13f59f3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5390,7 +5390,7 @@ F: drivers/media/video/*7146* F: include/media/*7146* SAMSUNG AUDIO (ASoC) DRIVERS -M: Jassi Brar +M: Jassi Brar L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: sound/soc/samsung -- cgit v1.2.3 From d69ac131384aa735192b63fd6e0abbe42dec68dc Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Thu, 14 Apr 2011 15:22:02 -0700 Subject: MAINTAINERS: add ARM/ts78xx-setup platform maintainer Signed-off-by: Alexander Clouter Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 649600cb8ec9..555a7bf8c525 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -877,6 +877,13 @@ F: arch/arm/mach-mv78xx0/ F: arch/arm/mach-orion5x/ F: arch/arm/plat-orion/ +ARM/Orion SoC/Technologic Systems TS-78xx platform support +M: Alexander Clouter +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +W: http://www.digriz.org.uk/ts78xx/kernel +S: Maintained +F: arch/arm/mach-orion5x/ts78xx-* + ARM/MIOA701 MACHINE SUPPORT M: Robert Jarzmik L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -- cgit v1.2.3 From 61bc02bb252d438a3bb12a236a141d222b35da7b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 14 Apr 2011 15:22:04 -0700 Subject: MAINTAINERS: update m68knommu patterns Commit 66d857b08b ("m68k: merge m68k and m68knommu arch directories") moved the files around. Signed-off-by: Joe Perches Acked-by: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 555a7bf8c525..e36170bcaeee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6255,7 +6255,8 @@ M: Greg Ungerer W: http://www.uclinux.org/ L: uclinux-dev@uclinux.org (subscribers-only) S: Maintained -F: arch/m68knommu/ +F: arch/m68k/*/*_no.* +F: arch/m68k/include/asm/*_no.* UCLINUX FOR RENESAS H8/300 (H8300) M: Yoshinori Sato -- cgit v1.2.3 From c897401bac2b099dd2ff673a9afe7193723d253c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 14 Apr 2011 15:22:05 -0700 Subject: MAINTAINERS: update various tty patterns Commits 4a6514e6d0 ("tty: move obsolete and broken tty drivers to drivers/staging/tty/") and a6afd9f3e8 ("tty: move a number of tty drivers from drivers/char/ to drivers/tty/") moved files around. Update patterns and orphan some files that were moved to staging. Signed-off-by: Joe Perches Cc: Greg Kroah-Hartman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e36170bcaeee..e205f6f8eb98 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -184,10 +184,9 @@ F: Documentation/filesystems/9p.txt F: fs/9p/ A2232 SERIAL BOARD DRIVER -M: Enver Haase L: linux-m68k@lists.linux-m68k.org -S: Maintained -F: drivers/char/ser_a2232* +S: Orphan +F: drivers/staging/generic_serial/ser_a2232* AACRAID SCSI RAID DRIVER M: Adaptec OEM Raid Solutions @@ -1830,11 +1829,10 @@ S: Maintained F: drivers/platform/x86/compal-laptop.c COMPUTONE INTELLIPORT MULTIPORT CARD -M: "Michael H. Warfield" W: http://www.wittsend.com/computone.html -S: Maintained +S: Orphan F: Documentation/serial/computone.txt -F: drivers/char/ip2/ +F: drivers/staging/tty/ip2/ CONEXANT ACCESSRUNNER USB DRIVER M: Simon Arlott @@ -2017,7 +2015,7 @@ F: drivers/net/wan/cycx* CYCLADES ASYNC MUX DRIVER W: http://www.cyclades.com/ S: Orphan -F: drivers/char/cyclades.c +F: drivers/tty/cyclades.c F: include/linux/cyclades.h CYCLADES PC300 DRIVER @@ -2131,8 +2129,8 @@ L: Eng.Linux@digi.com W: http://www.digi.com S: Orphan F: Documentation/serial/digiepca.txt -F: drivers/char/epca* -F: drivers/char/digi* +F: drivers/staging/tty/epca* +F: drivers/staging/tty/digi* DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck @@ -4199,7 +4197,7 @@ MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD M: Jiri Slaby S: Maintained F: Documentation/serial/moxa-smartio -F: drivers/char/mxser.* +F: drivers/tty/mxser.* MSI LAPTOP SUPPORT M: "Lee, Chun-Yi" @@ -4241,7 +4239,7 @@ F: sound/oss/msnd* MULTITECH MULTIPORT CARD (ISICOM) S: Orphan -F: drivers/char/isicom.c +F: drivers/tty/isicom.c F: include/linux/isicom.h MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER @@ -5280,14 +5278,14 @@ F: drivers/memstick/host/r592.* RISCOM8 DRIVER S: Orphan F: Documentation/serial/riscom8.txt -F: drivers/char/riscom8* +F: drivers/staging/tty/riscom8* ROCKETPORT DRIVER P: Comtrol Corp. W: http://www.comtrol.com S: Maintained F: Documentation/serial/rocket.txt -F: drivers/char/rocket* +F: drivers/tty/rocket* ROSE NETWORK LAYER M: Ralf Baechle @@ -5923,10 +5921,9 @@ F: arch/arm/mach-spear6xx/spear600.c F: arch/arm/mach-spear6xx/spear600_evb.c SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER -M: Roger Wolff -S: Supported +S: Orphan F: Documentation/serial/specialix.txt -F: drivers/char/specialix* +F: drivers/staging/tty/specialix* SPI SUBSYSTEM M: David Brownell -- cgit v1.2.3 From d00ebeac5f24f290636f7a895dafc124b2930a08 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 14 Apr 2011 15:22:07 -0700 Subject: MAINTAINERS: update STABLE BRANCH info Drop Chris Wright from STABLE maintainers. He hasn't done STABLE release work for quite some time. Signed-off-by: Randy Dunlap Acked-by: Chris Wright Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index e205f6f8eb98..17c22c50c691 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5968,7 +5968,6 @@ F: arch/alpha/kernel/srm_env.c STABLE BRANCH M: Greg Kroah-Hartman -M: Chris Wright L: stable@kernel.org S: Maintained -- cgit v1.2.3 From 6a534c9d265ebabca4c3ae6f8712fc5a27cd3999 Mon Sep 17 00:00:00 2001 From: "Hans J. Koch" Date: Thu, 14 Apr 2011 15:22:16 -0700 Subject: MAINTAINERS: change mail adress of Hans J. Koch My old mail address doesn't exist anymore. This patch changes all occurences in MAINTAINERS to my new address. Signed-off-by: Hans J. Koch Cc: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 17c22c50c691..ec3600306289 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1069,7 +1069,7 @@ F: arch/arm/mach-shmobile/ F: drivers/sh/ ARM/TELECHIPS ARM ARCHITECTURE -M: "Hans J. Koch" +M: "Hans J. Koch" L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/plat-tcc/ @@ -4082,7 +4082,7 @@ F: drivers/video/matrox/matroxfb_* F: include/linux/matroxfb.h MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER -M: "Hans J. Koch" +M: "Hans J. Koch" L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/max6650 @@ -6622,7 +6622,7 @@ F: fs/hostfs/ F: fs/hppfs/ USERSPACE I/O (UIO) -M: "Hans J. Koch" +M: "Hans J. Koch" M: Greg Kroah-Hartman S: Maintained F: Documentation/DocBook/uio-howto.tmpl -- cgit v1.2.3 From c8a75b345b38a9b63ffd077c0eb71707fa194fc6 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sun, 17 Apr 2011 17:46:40 -0700 Subject: r8169: add Realtek as maintainer. Per Hayes's request. Signed-off-by: Francois Romieu Signed-off-by: David S. Miller --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 649600cb8ec9..c85368d44094 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -151,6 +151,7 @@ S: Maintained F: drivers/net/hamradio/6pack.c 8169 10/100/1000 GIGABIT ETHERNET DRIVER +M: Realtek linux nic maintainers M: Francois Romieu L: netdev@vger.kernel.org S: Maintained -- cgit v1.2.3