summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig95
-rw-r--r--drivers/hwmon/Makefile10
-rw-r--r--drivers/hwmon/abituguru.c4
-rw-r--r--drivers/hwmon/abituguru3.c4
-rw-r--r--drivers/hwmon/ads1015.c337
-rw-r--r--drivers/hwmon/hp_accel.c405
-rw-r--r--drivers/hwmon/jz4740-hwmon.c4
-rw-r--r--drivers/hwmon/lis3lv02d.c1000
-rw-r--r--drivers/hwmon/lis3lv02d.h291
-rw-r--r--drivers/hwmon/lis3lv02d_i2c.c279
-rw-r--r--drivers/hwmon/lis3lv02d_spi.c145
-rw-r--r--drivers/hwmon/lm75.c66
-rw-r--r--drivers/hwmon/sch5627.c858
-rw-r--r--drivers/hwmon/sht15.c6
-rw-r--r--drivers/hwmon/twl4030-madc-hwmon.c157
15 files changed, 1440 insertions, 2221 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 1bfb4439e4e1..81131eda5544 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -521,7 +521,7 @@ config SENSORS_LM75
- Dallas Semiconductor DS75 and DS1775
- Maxim MAX6625 and MAX6626
- Microchip MCP980x
- - National Semiconductor LM75
+ - National Semiconductor LM75, LM75A
- NXP's LM75A
- ST Microelectronics STDS75
- TelCom (now Microchip) TCN75
@@ -959,6 +959,25 @@ 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_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
@@ -1028,6 +1047,16 @@ config SENSORS_TMP421
This driver can also be built as a module. If so, the module
will be called tmp421.
+config SENSORS_TWL4030_MADC
+ tristate "Texas Instruments TWL4030 MADC Hwmon"
+ depends on TWL4030_MADC
+ help
+ If you say yes here you get hwmon support for triton
+ TWL4030-MADC.
+
+ This driver can also be built as a module. If so it will be called
+ twl4030-madc-hwmon.
+
config SENSORS_VIA_CPUTEMP
tristate "VIA CPU temperature sensor"
depends on X86
@@ -1215,40 +1244,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 INPUT_POLLDEV
- 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 INPUT_POLLDEV
- 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
@@ -1296,36 +1291,6 @@ config SENSORS_ATK0110
This driver can also be built as a module. If so, the module
will be called asus_atk0110.
-config SENSORS_LIS3LV02D
- tristate "STMicroeletronics LIS3* three-axis digital accelerometer"
- depends on INPUT
- select INPUT_POLLDEV
- select NEW_LEDS
- select LEDS_CLASS
- default n
- help
- This driver provides support for the LIS3* accelerometers, such as the
- LIS3LV02DL or the LIS331DL. In particular, it can be found in a number
- of HP laptops, which have the "Mobile Data Protection System 3D" or
- "3D DriveGuard" feature. On such systems the driver should load
- automatically (via ACPI alias). The accelerometer might also be found
- in other systems, connected via SPI or I2C. The accelerometer data is
- readable via /sys/devices/platform/lis3lv02d.
-
- This driver also provides an absolute input class device, allowing
- a laptop to act as a pinball machine-esque joystick. It provides also
- a misc device which can be used to detect free-fall. On HP laptops,
- if the led infrastructure is activated, support for a led indicating
- disk protection will be provided as hp::hddprotect. For more
- information on the feature, refer to Documentation/hwmon/lis3lv02d.
-
- This driver can also be built as modules. If so, the core module
- will be called lis3lv02d and a specific module for HP laptops will be
- called hp_accel.
-
- Say Y here if you have an applicable laptop and want to experience
- the awesome power of lis3lv02d.
-
endif # ACPI
endif # HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index bd0410e4b44f..967d0ea9447f 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
@@ -63,9 +64,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_LIS3LV02D) += lis3lv02d.o hp_accel.o
-obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
-obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM70) += lm70.o
obj-$(CONFIG_SENSORS_LM73) += lm73.o
@@ -93,6 +91,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
@@ -105,6 +104,7 @@ obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
obj-$(CONFIG_SENSORS_TMP102) += tmp102.o
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
+obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o
obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
@@ -122,7 +122,5 @@ obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
-ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
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 <j.w.r.degoede@hhs.nl>
+ abituguru.c Copyright (c) 2005-2006 Hans de Goede <hdegoede@redhat.com>
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 <j.w.r.degoede@hhs.nl>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
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 <j.w.r.degoede@hhs.nl>
+ Copyright (c) 2006-2008 Hans de Goede <hdegoede@redhat.com>
Copyright (c) 2008 Alistair John Strachan <alistair@devzero.co.uk>
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 <j.w.r.degoede@hhs.nl>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Abit uGuru3 Sensor device");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c
new file mode 100644
index 000000000000..e9beeda4cbe5
--- /dev/null
+++ b/drivers/hwmon/ads1015.c
@@ -0,0 +1,337 @@
+/*
+ * ads1015.c - lm_sensors driver for ads1015 12-bit 4-input ADC
+ * (C) Copyright 2010
+ * Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <linux/i2c/ads1015.h>
+
+/* 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 };
+
+/* Data rates in samples per second */
+static const unsigned int data_rate_table[8] = {
+ 128, 250, 490, 920, 1600, 2400, 3300, 3300 };
+
+#define ADS1015_DEFAULT_CHANNELS 0xff
+#define ADS1015_DEFAULT_PGA 2
+#define ADS1015_DEFAULT_DATA_RATE 4
+
+struct ads1015_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock; /* mutex protect updates */
+ struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
+};
+
+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;
+ struct ads1015_data *data = i2c_get_clientdata(client);
+ unsigned int pga = data->channel_data[channel].pga;
+ int fullscale;
+ unsigned int data_rate = data->channel_data[channel].data_rate;
+ unsigned int conversion_time_ms;
+ int res;
+
+ mutex_lock(&data->update_lock);
+
+ /* get channel parameters */
+ res = ads1015_read_reg(client, ADS1015_CONFIG);
+ if (res < 0)
+ goto err_unlock;
+ config = res;
+ fullscale = fullscale_table[pga];
+ conversion_time_ms = DIV_ROUND_UP(1000, data_rate_table[data_rate]);
+
+ /* setup and start single conversion */
+ config &= 0x001f;
+ config |= (1 << 15) | (1 << 8);
+ config |= (channel & 0x0007) << 12;
+ config |= (pga & 0x0007) << 9;
+ config |= (data_rate & 0x0007) << 5;
+
+ res = ads1015_write_reg(client, ADS1015_CONFIG, config);
+ if (res < 0)
+ goto err_unlock;
+
+ /* wait until conversion finished */
+ msleep(conversion_time_ms);
+ res = ads1015_read_reg(client, ADS1015_CONFIG);
+ if (res < 0)
+ goto err_unlock;
+ config = res;
+ if (!(config & (1 << 15))) {
+ /* conversion not finished in time */
+ 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);
+}
+
+static const struct sensor_device_attribute ads1015_in[] = {
+ SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
+ SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
+ SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
+ SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
+ SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
+ SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
+ SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
+ SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
+};
+
+/*
+ * Driver interface
+ */
+
+static int ads1015_remove(struct i2c_client *client)
+{
+ struct ads1015_data *data = i2c_get_clientdata(client);
+ int k;
+
+ hwmon_device_unregister(data->hwmon_dev);
+ for (k = 0; k < ADS1015_CHANNELS; ++k)
+ device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
+ kfree(data);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static int ads1015_get_channels_config_of(struct i2c_client *client)
+{
+ struct ads1015_data *data = i2c_get_clientdata(client);
+ struct device_node *node;
+
+ if (!client->dev.of_node
+ || !of_get_next_child(client->dev.of_node, NULL))
+ return -EINVAL;
+
+ for_each_child_of_node(client->dev.of_node, node) {
+ const __be32 *property;
+ int len;
+ unsigned int channel;
+ unsigned int pga = ADS1015_DEFAULT_PGA;
+ unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
+
+ property = of_get_property(node, "reg", &len);
+ if (!property || len != sizeof(int)) {
+ dev_err(&client->dev, "invalid reg on %s\n",
+ node->full_name);
+ continue;
+ }
+
+ channel = be32_to_cpup(property);
+ if (channel > ADS1015_CHANNELS) {
+ dev_err(&client->dev,
+ "invalid channel index %d on %s\n",
+ channel, node->full_name);
+ continue;
+ }
+
+ property = of_get_property(node, "ti,gain", &len);
+ if (property && len == sizeof(int)) {
+ pga = be32_to_cpup(property);
+ if (pga > 6) {
+ dev_err(&client->dev,
+ "invalid gain on %s\n",
+ node->full_name);
+ }
+ }
+
+ property = of_get_property(node, "ti,datarate", &len);
+ if (property && len == sizeof(int)) {
+ data_rate = be32_to_cpup(property);
+ if (data_rate > 7) {
+ dev_err(&client->dev,
+ "invalid data_rate on %s\n",
+ node->full_name);
+ }
+ }
+
+ data->channel_data[channel].enabled = true;
+ data->channel_data[channel].pga = pga;
+ data->channel_data[channel].data_rate = data_rate;
+ }
+
+ return 0;
+}
+#endif
+
+static void ads1015_get_channels_config(struct i2c_client *client)
+{
+ unsigned int k;
+ struct ads1015_data *data = i2c_get_clientdata(client);
+ struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
+
+ /* prefer platform data */
+ if (pdata) {
+ memcpy(data->channel_data, pdata->channel_data,
+ sizeof(data->channel_data));
+ return;
+ }
+
+#ifdef CONFIG_OF
+ if (!ads1015_get_channels_config_of(client))
+ return;
+#endif
+
+ /* fallback on default configuration */
+ for (k = 0; k < ADS1015_CHANNELS; ++k) {
+ data->channel_data[k].enabled = true;
+ data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
+ data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE;
+ }
+}
+
+static int ads1015_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ads1015_data *data;
+ int err;
+ unsigned int k;
+
+ 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 */
+ ads1015_get_channels_config(client);
+ for (k = 0; k < ADS1015_CHANNELS; ++k) {
+ if (!data->channel_data[k].enabled)
+ continue;
+ err = device_create_file(&client->dev, &ads1015_in[k].dev_attr);
+ 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:
+ for (k = 0; k < ADS1015_CHANNELS; ++k)
+ device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
+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 <eibach@gdsys.de>");
+MODULE_DESCRIPTION("ADS1015 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_ads1015_init);
+module_exit(sensors_ads1015_exit);
diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c
deleted file mode 100644
index 3d21fa2b97cd..000000000000
--- a/drivers/hwmon/hp_accel.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS
- *
- * 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 <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/dmi.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/wait.h>
-#include <linux/poll.h>
-#include <linux/freezer.h>
-#include <linux/uaccess.h>
-#include <linux/leds.h>
-#include <acpi/acpi_drivers.h>
-#include <asm/atomic.h>
-#include "lis3lv02d.h"
-
-#define DRIVER_NAME "lis3lv02d"
-#define ACPI_MDPS_CLASS "accelerometer"
-
-/* Delayed LEDs infrastructure ------------------------------------ */
-
-/* Special LED class that can defer work */
-struct delayed_led_classdev {
- struct led_classdev led_classdev;
- struct work_struct work;
- enum led_brightness new_brightness;
-
- unsigned int led; /* For driver */
- void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value);
-};
-
-static inline void delayed_set_status_worker(struct work_struct *work)
-{
- struct delayed_led_classdev *data =
- container_of(work, struct delayed_led_classdev, work);
-
- data->set_brightness(data, data->new_brightness);
-}
-
-static inline void delayed_sysfs_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct delayed_led_classdev *data = container_of(led_cdev,
- struct delayed_led_classdev, led_classdev);
- data->new_brightness = brightness;
- schedule_work(&data->work);
-}
-
-/* HP-specific accelerometer driver ------------------------------------ */
-
-/* For automatic insertion of the module */
-static struct acpi_device_id lis3lv02d_device_ids[] = {
- {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);
-
-
-/**
- * lis3lv02d_acpi_init - ACPI _INI method: initialize the device.
- * @lis3: pointer to the device struct
- *
- * Returns 0 on success.
- */
-int lis3lv02d_acpi_init(struct lis3lv02d *lis3)
-{
- struct acpi_device *dev = lis3->bus_priv;
- if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI,
- NULL, NULL) != AE_OK)
- return -EINVAL;
-
- return 0;
-}
-
-/**
- * lis3lv02d_acpi_read - ACPI ALRD method: read a register
- * @lis3: pointer to the device struct
- * @reg: the register to read
- * @ret: result of the operation
- *
- * Returns 0 on success.
- */
-int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)
-{
- struct acpi_device *dev = lis3->bus_priv;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
- unsigned long long lret;
- acpi_status status;
-
- arg0.integer.value = reg;
-
- status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret);
- *ret = lret;
- return (status != AE_OK) ? -EINVAL : 0;
-}
-
-/**
- * lis3lv02d_acpi_write - ACPI ALWR method: write to a register
- * @lis3: pointer to the device struct
- * @reg: the register to write to
- * @val: the value to write
- *
- * Returns 0 on success.
- */
-int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)
-{
- struct acpi_device *dev = lis3->bus_priv;
- unsigned long long ret; /* Not used when writting */
- union acpi_object in_obj[2];
- struct acpi_object_list args = { 2, in_obj };
-
- in_obj[0].type = ACPI_TYPE_INTEGER;
- in_obj[0].integer.value = reg;
- in_obj[1].type = ACPI_TYPE_INTEGER;
- in_obj[1].integer.value = val;
-
- if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK)
- return -EINVAL;
-
- return 0;
-}
-
-static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi)
-{
- lis3_dev.ac = *((union axis_conversion *)dmi->driver_data);
- pr_info("hardware type %s found\n", dmi->ident);
-
- return 1;
-}
-
-/* Represents, for each axis seen by userspace, the corresponding hw axis (+1).
- * If the value is negative, the opposite of the hw value is used. */
-#define DEFINE_CONV(name, x, y, z) \
- static union axis_conversion lis3lv02d_axis_##name = \
- { .as_array = { x, y, z } }
-DEFINE_CONV(normal, 1, 2, 3);
-DEFINE_CONV(y_inverted, 1, -2, 3);
-DEFINE_CONV(x_inverted, -1, 2, 3);
-DEFINE_CONV(z_inverted, 1, 2, -3);
-DEFINE_CONV(xy_swap, 2, 1, 3);
-DEFINE_CONV(xy_rotated_left, -2, 1, 3);
-DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3);
-DEFINE_CONV(xy_swap_inverted, -2, -1, 3);
-DEFINE_CONV(xy_rotated_right, 2, -1, 3);
-DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3);
-
-#define AXIS_DMI_MATCH(_ident, _name, _axis) { \
- .ident = _ident, \
- .callback = lis3lv02d_dmi_matched, \
- .matches = { \
- DMI_MATCH(DMI_PRODUCT_NAME, _name) \
- }, \
- .driver_data = &lis3lv02d_axis_##_axis \
-}
-
-#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \
- _class2, _name2, \
- _axis) { \
- .ident = _ident, \
- .callback = lis3lv02d_dmi_matched, \
- .matches = { \
- DMI_MATCH(DMI_##_class1, _name1), \
- DMI_MATCH(DMI_##_class2, _name2), \
- }, \
- .driver_data = &lis3lv02d_axis_##_axis \
-}
-static struct dmi_system_id lis3lv02d_dmi_ids[] = {
- /* product names are truncated to match all kinds of a same model */
- AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted),
- AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted),
- AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted),
- AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted),
- AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted),
- AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap),
- AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted),
- AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left),
- AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted),
- AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd),
- AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd),
- AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap),
- AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right),
- AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted),
- AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted),
- AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right),
- AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap),
- /* Intel-based HP Pavilion dv5 */
- AXIS_DMI_MATCH2("HPDV5_I",
- PRODUCT_NAME, "HP Pavilion dv5",
- BOARD_NAME, "3603",
- x_inverted),
- /* AMD-based HP Pavilion dv5 */
- AXIS_DMI_MATCH2("HPDV5_A",
- PRODUCT_NAME, "HP Pavilion dv5",
- BOARD_NAME, "3600",
- y_inverted),
- AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted),
- AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted),
- AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted),
- AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left),
- AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left),
- AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
- AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
- AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
- AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd),
- { NULL, }
-/* Laptop models without axis info (yet):
- * "NC6910" "HP Compaq 6910"
- * "NC2400" "HP Compaq nc2400"
- * "NX74x0" "HP Compaq nx74"
- * "NX6325" "HP Compaq nx6325"
- * "NC4400" "HP Compaq nc4400"
- */
-};
-
-static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value)
-{
- struct acpi_device *dev = lis3_dev.bus_priv;
- unsigned long long ret; /* Not used when writing */
- union acpi_object in_obj[1];
- struct acpi_object_list args = { 1, in_obj };
-
- in_obj[0].type = ACPI_TYPE_INTEGER;
- in_obj[0].integer.value = !!value;
-
- acpi_evaluate_integer(dev->handle, "ALED", &args, &ret);
-}
-
-static struct delayed_led_classdev hpled_led = {
- .led_classdev = {
- .name = "hp::hddprotect",
- .default_trigger = "none",
- .brightness_set = delayed_sysfs_set,
- .flags = LED_CORE_SUSPENDRESUME,
- },
- .set_brightness = hpled_set,
-};
-
-static acpi_status
-lis3lv02d_get_resource(struct acpi_resource *resource, void *context)
-{
- if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
- struct acpi_resource_extended_irq *irq;
- u32 *device_irq = context;
-
- irq = &resource->data.extended_irq;
- *device_irq = irq->interrupts[0];
- }
-
- return AE_OK;
-}
-
-static void lis3lv02d_enum_resources(struct acpi_device *device)
-{
- acpi_status status;
-
- status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
- lis3lv02d_get_resource, &lis3_dev.irq);
- if (ACPI_FAILURE(status))
- printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n");
-}
-
-static int lis3lv02d_add(struct acpi_device *device)
-{
- int ret;
-
- if (!device)
- return -EINVAL;
-
- lis3_dev.bus_priv = device;
- lis3_dev.init = lis3lv02d_acpi_init;
- lis3_dev.read = lis3lv02d_acpi_read;
- lis3_dev.write = lis3lv02d_acpi_write;
- strcpy(acpi_device_name(device), DRIVER_NAME);
- strcpy(acpi_device_class(device), ACPI_MDPS_CLASS);
- device->driver_data = &lis3_dev;
-
- /* obtain IRQ number of our device from ACPI */
- lis3lv02d_enum_resources(device);
-
- /* If possible use a "standard" axes order */
- if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) {
- pr_info("Using custom axes %d,%d,%d\n",
- lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z);
- } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) {
- pr_info("laptop model unknown, using default axes configuration\n");
- lis3_dev.ac = lis3lv02d_axis_normal;
- }
-
- /* call the core layer do its init */
- ret = lis3lv02d_init_device(&lis3_dev);
- if (ret)
- return ret;
-
- INIT_WORK(&hpled_led.work, delayed_set_status_worker);
- ret = led_classdev_register(NULL, &hpled_led.led_classdev);
- if (ret) {
- lis3lv02d_joystick_disable();
- lis3lv02d_poweroff(&lis3_dev);
- flush_work(&hpled_led.work);
- return ret;
- }
-
- return ret;
-}
-
-static int lis3lv02d_remove(struct acpi_device *device, int type)
-{
- if (!device)
- return -EINVAL;
-
- lis3lv02d_joystick_disable();
- lis3lv02d_poweroff(&lis3_dev);
-
- led_classdev_unregister(&hpled_led.led_classdev);
- flush_work(&hpled_led.work);
-
- return lis3lv02d_remove_fs(&lis3_dev);
-}
-
-
-#ifdef CONFIG_PM
-static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state)
-{
- /* make sure the device is off when we suspend */
- lis3lv02d_poweroff(&lis3_dev);
- return 0;
-}
-
-static int lis3lv02d_resume(struct acpi_device *device)
-{
- lis3lv02d_poweron(&lis3_dev);
- return 0;
-}
-#else
-#define lis3lv02d_suspend NULL
-#define lis3lv02d_resume NULL
-#endif
-
-/* For the HP MDPS aka 3D Driveguard */
-static struct acpi_driver lis3lv02d_driver = {
- .name = DRIVER_NAME,
- .class = ACPI_MDPS_CLASS,
- .ids = lis3lv02d_device_ids,
- .ops = {
- .add = lis3lv02d_add,
- .remove = lis3lv02d_remove,
- .suspend = lis3lv02d_suspend,
- .resume = lis3lv02d_resume,
- }
-};
-
-static int __init lis3lv02d_init_module(void)
-{
- int ret;
-
- if (acpi_disabled)
- return -ENODEV;
-
- ret = acpi_bus_register_driver(&lis3lv02d_driver);
- if (ret < 0)
- return ret;
-
- pr_info("driver loaded\n");
-
- return 0;
-}
-
-static void __exit lis3lv02d_exit_module(void)
-{
- acpi_bus_unregister_driver(&lis3lv02d_driver);
-}
-
-MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED.");
-MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
-MODULE_LICENSE("GPL");
-
-module_init(lis3lv02d_init_module);
-module_exit(lis3lv02d_exit_module);
-
diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c
index 1c8b3d9e2051..fea292d43407 100644
--- a/drivers/hwmon/jz4740-hwmon.c
+++ b/drivers/hwmon/jz4740-hwmon.c
@@ -32,7 +32,7 @@ struct jz4740_hwmon {
int irq;
- struct mfd_cell *cell;
+ const struct mfd_cell *cell;
struct device *hwmon;
struct completion read_completion;
@@ -112,7 +112,7 @@ static int __devinit jz4740_hwmon_probe(struct platform_device *pdev)
return -ENOMEM;
}
- hwmon->cell = pdev->dev.platform_data;
+ hwmon->cell = mfd_get_cell(pdev);
hwmon->irq = platform_get_irq(pdev, 0);
if (hwmon->irq < 0) {
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 <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/dmi.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/input-polldev.h>
-#include <linux/delay.h>
-#include <linux/wait.h>
-#include <linux/poll.h>
-#include <linux/slab.h>
-#include <linux/freezer.h>
-#include <linux/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pm_runtime.h>
-#include <asm/atomic.h>
-#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, &reg);
- 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, &reg);
- 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 <linux/platform_device.h>
-#include <linux/input-polldev.h>
-#include <linux/regulator/consumer.h>
-
-/*
- * 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 <linux/lis3lv02d.h>
-
-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 <daniel@caiaq.de> ).
- *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
- *
- * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
- *
- * 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 <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/pm_runtime.h>
-#include <linux/delay.h>
-#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, &reg);
- if (reg != lis3->whoami)
- printk(KERN_ERR "lis3: power on failure\n");
-
- /* power up the device */
- ret = lis3->read(lis3, CTRL_REG1, &reg);
- 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 <daniel@caiaq.de>
- *
- * 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 <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/input.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/spi/spi.h>
-#include <linux/pm.h>
-
-#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, &reg);
- 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 <daniel@caiaq.de>");
-MODULE_DESCRIPTION("lis3lv02d SPI glue layer");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("spi:" DRV_NAME);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index f36eb80d227f..ef902d5d06ab 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -232,13 +232,16 @@ static const struct i2c_device_id lm75_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, lm75_ids);
+#define LM75A_ID 0xA1
+
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm75_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
int i;
- int cur, conf, hyst, os;
+ int conf, hyst, os;
+ bool is_lm75a = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
@@ -250,37 +253,58 @@ static int lm75_detect(struct i2c_client *new_client,
addresses 0x04-0x07 returning the last read value.
The cycling+unused addresses combination is not tested,
since it would significantly slow the detection down and would
- hardly add any value. */
+ hardly add any value.
- /* Unused addresses */
- cur = i2c_smbus_read_word_data(new_client, 0);
- conf = i2c_smbus_read_byte_data(new_client, 1);
- hyst = i2c_smbus_read_word_data(new_client, 2);
- if (i2c_smbus_read_word_data(new_client, 4) != hyst
- || i2c_smbus_read_word_data(new_client, 5) != hyst
- || i2c_smbus_read_word_data(new_client, 6) != hyst
- || i2c_smbus_read_word_data(new_client, 7) != hyst)
- return -ENODEV;
- os = i2c_smbus_read_word_data(new_client, 3);
- if (i2c_smbus_read_word_data(new_client, 4) != os
- || i2c_smbus_read_word_data(new_client, 5) != os
- || i2c_smbus_read_word_data(new_client, 6) != os
- || i2c_smbus_read_word_data(new_client, 7) != os)
- return -ENODEV;
+ The National Semiconductor LM75A is different than earlier
+ LM75s. It has an ID byte of 0xaX (where X is the chip
+ revision, with 1 being the only revision in existence) in
+ register 7, and unused registers return 0xff rather than the
+ last read value. */
/* Unused bits */
+ conf = i2c_smbus_read_byte_data(new_client, 1);
if (conf & 0xe0)
return -ENODEV;
+ /* First check for LM75A */
+ if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) {
+ /* LM75A returns 0xff on unused registers so
+ just to be sure we check for that too. */
+ if (i2c_smbus_read_byte_data(new_client, 4) != 0xff
+ || i2c_smbus_read_byte_data(new_client, 5) != 0xff
+ || i2c_smbus_read_byte_data(new_client, 6) != 0xff)
+ return -ENODEV;
+ is_lm75a = 1;
+ hyst = i2c_smbus_read_byte_data(new_client, 2);
+ os = i2c_smbus_read_byte_data(new_client, 3);
+ } else { /* Traditional style LM75 detection */
+ /* Unused addresses */
+ hyst = i2c_smbus_read_byte_data(new_client, 2);
+ if (i2c_smbus_read_byte_data(new_client, 4) != hyst
+ || i2c_smbus_read_byte_data(new_client, 5) != hyst
+ || i2c_smbus_read_byte_data(new_client, 6) != hyst
+ || i2c_smbus_read_byte_data(new_client, 7) != hyst)
+ return -ENODEV;
+ os = i2c_smbus_read_byte_data(new_client, 3);
+ if (i2c_smbus_read_byte_data(new_client, 4) != os
+ || i2c_smbus_read_byte_data(new_client, 5) != os
+ || i2c_smbus_read_byte_data(new_client, 6) != os
+ || i2c_smbus_read_byte_data(new_client, 7) != os)
+ return -ENODEV;
+ }
+
/* Addresses cycling */
- for (i = 8; i < 0xff; i += 8) {
+ for (i = 8; i <= 248; i += 40) {
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
- || i2c_smbus_read_word_data(new_client, i + 2) != hyst
- || i2c_smbus_read_word_data(new_client, i + 3) != os)
+ || i2c_smbus_read_byte_data(new_client, i + 2) != hyst
+ || i2c_smbus_read_byte_data(new_client, i + 3) != os)
+ return -ENODEV;
+ if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7)
+ != LM75A_ID)
return -ENODEV;
}
- strlcpy(info->type, "lm75", I2C_NAME_SIZE);
+ strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE);
return 0;
}
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 <hdegoede@redhat.com> *
+ * *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+
+#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);
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index a610e7880fb3..1a9c32d6893a 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -333,11 +333,11 @@ static inline int sht15_calc_humid(struct sht15_data *data)
const int c1 = -4;
const int c2 = 40500; /* x 10 ^ -6 */
- const int c3 = -2800; /* x10 ^ -9 */
+ const int c3 = -28; /* x 10 ^ -7 */
RHlinear = c1*1000
+ c2 * data->val_humid/1000
- + (data->val_humid * data->val_humid * c3)/1000000;
+ + (data->val_humid * data->val_humid * c3) / 10000;
return (temp - 25000) * (10000 + 80 * data->val_humid)
/ 1000000 + RHlinear;
}
@@ -610,7 +610,7 @@ static int __devexit sht15_remove(struct platform_device *pdev)
struct sht15_data *data = platform_get_drvdata(pdev);
/* Make sure any reads from the device are done and
- * prevent new ones beginnning */
+ * prevent new ones from beginning */
mutex_lock(&data->read_lock);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
diff --git a/drivers/hwmon/twl4030-madc-hwmon.c b/drivers/hwmon/twl4030-madc-hwmon.c
new file mode 100644
index 000000000000..97e22bef85ab
--- /dev/null
+++ b/drivers/hwmon/twl4030-madc-hwmon.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * TWL4030 MADC Hwmon driver-This driver monitors the real time
+ * conversion of analog signals like battery temperature,
+ * battery type, battery level etc. User can ask for the conversion on a
+ * particular channel using the sysfs nodes.
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * J Keerthy <j-keerthy@ti.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c/twl.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/stddef.h>
+#include <linux/sysfs.h>
+#include <linux/err.h>
+#include <linux/types.h>
+
+/*
+ * sysfs hook function
+ */
+static ssize_t madc_read(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct twl4030_madc_request req;
+ long val;
+
+ req.channels = (1 << attr->index);
+ req.method = TWL4030_MADC_SW2;
+ req.func_cb = NULL;
+ val = twl4030_madc_conversion(&req);
+ if (val < 0)
+ return val;
+
+ return sprintf(buf, "%d\n", req.rbuf[attr->index]);
+}
+
+/* sysfs nodes to read individual channels from user side */
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, madc_read, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, madc_read, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, madc_read, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, madc_read, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, madc_read, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, madc_read, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, madc_read, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, madc_read, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, madc_read, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, madc_read, NULL, 9);
+static SENSOR_DEVICE_ATTR(curr10_input, S_IRUGO, madc_read, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, madc_read, NULL, 11);
+static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, madc_read, NULL, 12);
+static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, madc_read, NULL, 15);
+
+static struct attribute *twl4030_madc_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_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,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_curr10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group twl4030_madc_group = {
+ .attrs = twl4030_madc_attributes,
+};
+
+static int __devinit twl4030_madc_hwmon_probe(struct platform_device *pdev)
+{
+ int ret;
+ int status;
+ struct device *hwmon;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &twl4030_madc_group);
+ if (ret)
+ goto err_sysfs;
+ hwmon = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(hwmon)) {
+ dev_err(&pdev->dev, "hwmon_device_register failed.\n");
+ status = PTR_ERR(hwmon);
+ goto err_reg;
+ }
+
+ return 0;
+
+err_reg:
+ sysfs_remove_group(&pdev->dev.kobj, &twl4030_madc_group);
+err_sysfs:
+
+ return ret;
+}
+
+static int __devexit twl4030_madc_hwmon_remove(struct platform_device *pdev)
+{
+ hwmon_device_unregister(&pdev->dev);
+ sysfs_remove_group(&pdev->dev.kobj, &twl4030_madc_group);
+
+ return 0;
+}
+
+static struct platform_driver twl4030_madc_hwmon_driver = {
+ .probe = twl4030_madc_hwmon_probe,
+ .remove = __exit_p(twl4030_madc_hwmon_remove),
+ .driver = {
+ .name = "twl4030_madc_hwmon",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init twl4030_madc_hwmon_init(void)
+{
+ return platform_driver_register(&twl4030_madc_hwmon_driver);
+}
+
+module_init(twl4030_madc_hwmon_init);
+
+static void __exit twl4030_madc_hwmon_exit(void)
+{
+ platform_driver_unregister(&twl4030_madc_hwmon_driver);
+}
+
+module_exit(twl4030_madc_hwmon_exit);
+
+MODULE_DESCRIPTION("TWL4030 ADC Hwmon driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("J Keerthy");
+MODULE_ALIAS("twl4030_madc_hwmon");