1837 lines
52 KiB
Diff
1837 lines
52 KiB
Diff
From cffaf6b15ff40cfbeafd0d4728ba3a5a5fb6155f Mon Sep 17 00:00:00 2001
|
|
From: Alan Olsen <alan.r.olsen@intel.com>
|
|
Date: Thu, 15 Oct 2009 14:26:47 -0700
|
|
Subject: [PATCH 062/104] Moorestown Sensor drivers v1.1 consolidated patch
|
|
|
|
This patch contains the following patches:
|
|
|
|
Alpha2-1.1-1-5-mrst-Sensors-ALS-Driver-for-Moorestown.patch
|
|
|
|
[PATCH] ALS Driver for Moorestown Sensors
|
|
|
|
This patch single patch for Alpha2:2.0. ALS driver will read
|
|
the latest Lux measurement based on the light brightness and
|
|
will report the LUX output through sysfs interface.
|
|
|
|
Signed-off-by: Kalhan Trisal <kalhan.trisal@intel.com>
|
|
|
|
Alpha2-1.1-2-5-mrst-Sensors-Compass-Driver-for-Moorestown.patch
|
|
|
|
[PATCH] Compass Driver for Moorestown Sensors
|
|
This patch single patch for Alpha2:2.0.This driver will report
|
|
the heading values in degrees to the sysfs interface.The vlaues
|
|
returned are head . e.g. 245.6
|
|
|
|
Signed-off-by: Kalhan Trisal <kalhan.trisal@intel.com>
|
|
|
|
Alpha2-1.1-3-5-mrst-Sensors-Accelerometer-Driver-for-Moorestown.patch
|
|
|
|
[PATCH] Accelerometer Driver for Moorestown Sensors
|
|
|
|
This patch single patch for Alpha2:2.0.Accelerometer driver will
|
|
read the x,y,z coordinate registers and provide the information to
|
|
user through sysfs interface.
|
|
|
|
Signed-off-by: Kalhan Trisal <kalhan.trisal@intel.com>
|
|
|
|
Alpha2-1.1-4-5-mrst-Sensors-Vibrator-Driver-for-Moorestown.patch
|
|
|
|
[PATCH] Vibrator Driver for Moorestown Sensors
|
|
|
|
This patch single patch for Alpha2:2.0.Vibrator can be switched
|
|
on/off using sysfs interface.
|
|
|
|
Signed-off-by: Kalhan Trisal <kalhan.trisal@intel.com>
|
|
|
|
Alpha2-1.1-5-5-mrst-Sensors-Thermal-Driver-for-Moorestown.patch
|
|
|
|
[PATCH] Thermal Driver for Moorestown Sensors
|
|
|
|
Moorestown Platform has EMC1403 chip which support three thermal
|
|
devices, one thermal zone is used by the EMC1403 chip itself and
|
|
second is used by processor and third one is used for platform
|
|
(skin temperature).Driver support poll and interrupt
|
|
mode,min/max/crit configuration can be done using sysfs interface.
|
|
|
|
The driver also support interrupt mode when the temperature crosses the
|
|
threshold configured value the min/max/crit. ALERT/THERM interrupt will
|
|
be triggered and driver register its callback with GPE driver, and send
|
|
the events to OSPM power management to take action. OSPM will take
|
|
action and set the new threshold values till it doesnot get ALERT/THERM
|
|
events.temp1 is used for configuring internal EMC1403 chip diode, temp2
|
|
is used to configure processor diode and temp3 is used to configure the
|
|
platform diode.
|
|
|
|
The interrupt mode code has dependency MRST PMIC_GPIO/MAX7315/OSPM.Flag
|
|
is added to differentiate the generic functionality of the driver with
|
|
moorestown specific.
|
|
|
|
Signed-off-by: Kalhan Trisal <kalhan.trisal at intel.com>
|
|
|
|
Signed-off-by: Alan Olsen <alan.r.olsen@intel.com>
|
|
---
|
|
drivers/hwmon/Kconfig | 35 ++
|
|
drivers/hwmon/Makefile | 4
|
|
drivers/hwmon/emc1403.c | 731 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
drivers/hwmon/hmc6352.c | 250 ++++++++++++++++
|
|
drivers/hwmon/isl29020.c | 248 +++++++++++++++
|
|
drivers/hwmon/lis331dl.c | 322 ++++++++++++++++++++
|
|
drivers/misc/Kconfig | 7
|
|
drivers/misc/Makefile | 1
|
|
drivers/misc/mrst_vib.c | 99 ++++++
|
|
9 files changed, 1697 insertions(+)
|
|
create mode 100644 drivers/hwmon/emc1403.c
|
|
create mode 100644 drivers/hwmon/hmc6352.c
|
|
create mode 100644 drivers/hwmon/isl29020.c
|
|
create mode 100644 drivers/hwmon/lis331dl.c
|
|
create mode 100644 drivers/misc/mrst_vib.c
|
|
|
|
--- a/drivers/hwmon/Kconfig
|
|
+++ b/drivers/hwmon/Kconfig
|
|
@@ -28,6 +28,41 @@ config HWMON_VID
|
|
tristate
|
|
default n
|
|
|
|
+config SENSORS_ISL29020
|
|
+ tristate "Intersil ISL29020 ALS"
|
|
+ depends on I2C_MRST
|
|
+ help
|
|
+ If you say yes here you get support for the ALS Devices
|
|
+ Ambient Light Sensor monitoring chip.
|
|
+ Range values can be configured using sysfs.
|
|
+ Lux Data are accessible via sysfs.
|
|
+
|
|
+config SENSORS_HMC6352
|
|
+ tristate "Honeywell HMC6352 compass"
|
|
+ depends on I2C_MRST
|
|
+ help
|
|
+ If you say yes here you get support for the Compass Devices
|
|
+ Device can be configured using sysfs.
|
|
+ heading data can be accessible via sysfs.
|
|
+
|
|
+config SENSORS_LIS331DL
|
|
+ tristate "STMicroeletronics LIS331DL three-axis digital accelerometer"
|
|
+ depends on I2C_MRST
|
|
+ help
|
|
+ If you say yes here you get support for the Accelerometer Devices
|
|
+ Device can be configured using sysfs.
|
|
+ x y Z data can be accessible via sysfs.
|
|
+
|
|
+config SENSORS_EMC1403
|
|
+ tristate "SMSC EMC1403 Thermal"
|
|
+ depends on I2C_MRST && GPE && GPIO_MAX7315 && MSTWN_POWER_MGMT
|
|
+ help
|
|
+ If you say yes here you get support for the SMSC Devices
|
|
+ EMC1403 temperature monitoring chip.
|
|
+
|
|
+ Threshold values can be configured using sysfs.
|
|
+ Data from the different diode are accessible via sysfs.
|
|
+
|
|
config HWMON_DEBUG_CHIP
|
|
bool "Hardware Monitoring Chip debugging messages"
|
|
default n
|
|
--- a/drivers/hwmon/Makefile
|
|
+++ b/drivers/hwmon/Makefile
|
|
@@ -99,6 +99,10 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l7
|
|
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
|
|
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
|
|
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
|
|
+obj-$(CONFIG_SENSORS_ISL29020) += isl29020.o
|
|
+obj-$(CONFIG_SENSORS_HMC6352) += hmc6352.o
|
|
+obj-$(CONFIG_SENSORS_LIS331DL) += lis331dl.o
|
|
+obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
|
|
|
|
ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
|
|
EXTRA_CFLAGS += -DDEBUG
|
|
--- /dev/null
|
|
+++ b/drivers/hwmon/emc1403.c
|
|
@@ -0,0 +1,731 @@
|
|
+/*
|
|
+ * emc1403.c - SMSC Thermal Driver
|
|
+ *
|
|
+ * Copyright (C) 2008 Intel Corp
|
|
+ *
|
|
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+ *
|
|
+ * 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; version 2 of the License.
|
|
+ *
|
|
+ * 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/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/hwmon.h>
|
|
+#include <linux/hwmon-sysfs.h>
|
|
+#include <linux/hwmon-vid.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/workqueue.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <linux/gpe.h>
|
|
+#include <linux/intel_mid.h>
|
|
+
|
|
+
|
|
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
|
|
+MODULE_DESCRIPTION("emc1403 Thermal Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|
|
+/* To support the interrupt mechanism for moorestown interrupt flag is added
|
|
+ * If the flag is not enabled it support generic emc1403 chip */
|
|
+
|
|
+#if defined(CONFIG_GPIO_LNWPMIC) && defined(CONFIG_GPIO_MAX7315) \
|
|
+ && defined(CONFIG_MSTWN_POWER_MGMT)
|
|
+#define MOORESTOWN_INTERRUPT_ENABLE
|
|
+#endif
|
|
+
|
|
+/* Limit status reg Therm/High/Low/Fault*/
|
|
+static const u8 THM_STAT_REG_TEMP[] = { 0x37, 0x35, 0x36, 0x1B, 0x02};
|
|
+
|
|
+/* Channel diode temp set */
|
|
+static const u8 THM_CHAN_TEMP[] = { 0x10, 0x08, 0x04, 0x02, 0x01 };
|
|
+
|
|
+/* Therm Limit reg store values */
|
|
+static const u8 THM_LIMIT_REG_TEMP[] = { 0x05, 0x06, 0x07, 0x08, 0x15, 0x16,
|
|
+ 0x19, 0x1A, 0x20, 0x21 };
|
|
+
|
|
+/* DATA REGISTERS */
|
|
+static const u8 THM_REG_CURR_TEMP[] = { 0x00, 0x01, 0x23 };
|
|
+
|
|
+#define THERMAL_PID_REG 0xfd
|
|
+#define THERMAL_SMSC_ID_REG 0xfe
|
|
+#define THERMAL_REVISION_REG 0xff
|
|
+#define THERMAL_ADC_UPDATE_BUSY 0x80
|
|
+#define I2C_THERMAL_SLAVE_ADDR 0x4C
|
|
+#define TEMP1 1
|
|
+#define TEMP2 2
|
|
+#define TEMP3 4
|
|
+#define IRQ_TYPE_MASK (1 << 15)
|
|
+#define HIGH_EVENT 1
|
|
+#define LOW_EVENT 2
|
|
+#define THERM_EVENT 3
|
|
+#define FAULT_EVENT 4
|
|
+#define ALERT_EVENT 1
|
|
+#define POWER_STA_ENABLE 0
|
|
+#define POWER_STA_DISABLE 1
|
|
+#define INTERRUPT_MODE_ENABLE 0
|
|
+#define INTERRUPT_MODE_DISABLE 1
|
|
+
|
|
+struct thermal_data {
|
|
+ struct i2c_client *client;
|
|
+ struct device *hwmon_dev;
|
|
+ int therm_irq;
|
|
+ int alert_irq;
|
|
+ struct work_struct therm_handler;
|
|
+ struct work_struct alert_handler;
|
|
+};
|
|
+
|
|
+static unsigned int i2c_read_current_data(struct i2c_client *client, u8 reg)
|
|
+{
|
|
+ unsigned int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_read_byte_data(client, reg);
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+static unsigned int i2c_write_current_data(struct i2c_client *client,
|
|
+ unsigned int reg, unsigned int value)
|
|
+{
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_write_byte_data(client, reg, value);
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+static int calculate_offset(int type, int temp_ofs)
|
|
+{
|
|
+ int offset = 0;
|
|
+
|
|
+ switch (type) {
|
|
+ case TEMP1:
|
|
+ if (temp_ofs == 0)
|
|
+ offset = 1;
|
|
+ else if (temp_ofs == 1)
|
|
+ offset = 0;
|
|
+ else if (temp_ofs == 2)
|
|
+ offset = 8;
|
|
+ break;
|
|
+ case TEMP2:
|
|
+ if (temp_ofs == 0)
|
|
+ offset = 3;
|
|
+ else if (temp_ofs == 1)
|
|
+ offset = 2;
|
|
+ else if (temp_ofs == 2)
|
|
+ offset = 6;
|
|
+ break;
|
|
+ case TEMP3:
|
|
+ if (temp_ofs == 0)
|
|
+ offset = 5;
|
|
+ else if (temp_ofs == 1)
|
|
+ offset = 4;
|
|
+ else if (temp_ofs == 2)
|
|
+ offset = 7;
|
|
+ break;
|
|
+ default:
|
|
+ offset = -1;
|
|
+ printk(KERN_WARNING "emc1403: Invalid arg \n");
|
|
+ break;
|
|
+ }
|
|
+ return offset;
|
|
+
|
|
+}
|
|
+
|
|
+#ifdef MOORESTOWN_INTERRUPT_ENABLE
|
|
+static void status_reg_read(struct i2c_client *client)
|
|
+{
|
|
+ i2c_read_current_data(client, 0x36);
|
|
+ i2c_read_current_data(client, 0x35);
|
|
+ i2c_read_current_data(client, 0x1B);
|
|
+}
|
|
+
|
|
+/* when the thermal governor takes action we unmask the bit
|
|
+ * if the temp is lower tham threshold values then no new event will
|
|
+ * be raised else if the current temperature is still high the interrupt
|
|
+ * will be sent again */
|
|
+
|
|
+static void reg_unmask_intr(struct i2c_client *client, int offset,
|
|
+ int value)
|
|
+{
|
|
+ u8 ret_val, set_mask, ret = 0, alert = 0;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, 0x1F);
|
|
+ if (offset == 6 || offset == 7 || offset == 8) {
|
|
+ ret = i2c_read_current_data(client, 0x37); /* Themal status */
|
|
+ } else if (offset == 2 || offset == 3) {
|
|
+ if (((ret_val >> 1) & 1)) {
|
|
+ set_mask = (ret_val & 0x05);
|
|
+ alert = 1;
|
|
+ }
|
|
+ } else if (offset == 4 || offset == 5) {
|
|
+ if (((ret_val >> 2) & 1)) {
|
|
+ set_mask = (ret_val & 0x03);
|
|
+ alert = 1;
|
|
+ }
|
|
+ } else if (offset == 0 || offset == 1) {
|
|
+ if (ret_val & 1) {
|
|
+ set_mask = (ret_val & 0x06);
|
|
+ alert = 1;
|
|
+ }
|
|
+ }
|
|
+ /* only rest set the mask for alert events */
|
|
+ if (alert == 1) {
|
|
+ status_reg_read(client);
|
|
+ i2c_write_current_data(client, 0x1F, set_mask);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+static ssize_t show_temp_auto_offset(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct sensor_device_attribute_2 *s_attr = to_sensor_dev_attr_2(attr);
|
|
+ int temp_index = s_attr->index;
|
|
+ int temp_ofs = s_attr->nr;
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val = 0;
|
|
+ int ret_offset = 0;
|
|
+
|
|
+ ret_offset = calculate_offset(temp_index, temp_ofs);
|
|
+ if (ret_offset != -1) {
|
|
+ ret_val = i2c_read_current_data(client,
|
|
+ THM_LIMIT_REG_TEMP[ret_offset]);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static ssize_t store_temp_auto_offset(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct sensor_device_attribute_2 *s_attr = to_sensor_dev_attr_2(attr);
|
|
+ int temp_index = s_attr->index;
|
|
+ int temp_ofs = s_attr->nr;
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned long val;
|
|
+ int ret_offset = 0;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ ret_offset = calculate_offset(temp_index, temp_ofs);
|
|
+ if (ret_offset != -1) {
|
|
+ i2c_write_current_data(client,
|
|
+ THM_LIMIT_REG_TEMP[ret_offset], val);
|
|
+#ifdef MOORESTOWN_INTERRUPT_ENABLE
|
|
+ reg_unmask_intr(client, ret_offset, val);
|
|
+#endif
|
|
+ return count;
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static ssize_t show_temp_hyst(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, THM_LIMIT_REG_TEMP[9]);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t store_temp_hyst(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned long val = 0;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ i2c_write_current_data(client, THM_LIMIT_REG_TEMP[9], val);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t show_temp1_curr_temp(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, THM_REG_CURR_TEMP[0]);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t show_temp2_curr_temp(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, THM_REG_CURR_TEMP[1]);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t show_temp3_curr_temp(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, THM_REG_CURR_TEMP[2]);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t show_status_reg(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val1, ret_val2, ret_val3, ret_val4;
|
|
+
|
|
+ ret_val1 = i2c_read_current_data(client, 0x1F);
|
|
+ ret_val2 = i2c_read_current_data(client, 0x35);
|
|
+ ret_val3 = i2c_read_current_data(client, 0x36);
|
|
+ ret_val4 = i2c_read_current_data(client, 0x37);
|
|
+ return sprintf(buf, "alarm=%x,High=%x,Low=%x,Therm=%x \n",
|
|
+ ret_val1, ret_val2, ret_val3, ret_val4);
|
|
+}
|
|
+
|
|
+static ssize_t show_power_state(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, 0x03);
|
|
+ ret_val = ret_val & 0x40;
|
|
+ if (ret_val == 0x40)
|
|
+ ret_val = 1;
|
|
+ return sprintf(buf, "%x", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t store_power_state(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned long val = 0;
|
|
+ char curr_val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+
|
|
+ curr_val = i2c_read_current_data(client, 0x03);
|
|
+ if (val == POWER_STA_ENABLE)
|
|
+ curr_val = curr_val & 0xBF;
|
|
+ else if (val == POWER_STA_DISABLE)
|
|
+ curr_val = curr_val | 0x40;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+ i2c_write_current_data(client, 0x03, curr_val);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t show_mode(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_read_current_data(client, 0x03);
|
|
+ ret_val = ret_val & 0x80;
|
|
+ if (ret_val == 0x80)
|
|
+ ret_val = 1;
|
|
+ return sprintf(buf, "%x", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t store_mode(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned long val = 0;
|
|
+ char curr_val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+
|
|
+ curr_val = i2c_read_current_data(client, 0x03);
|
|
+ if (val == INTERRUPT_MODE_ENABLE)
|
|
+ curr_val = curr_val & 0x7F;
|
|
+ else if (val == INTERRUPT_MODE_DISABLE)
|
|
+ curr_val = curr_val | 0x80;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+ i2c_write_current_data(client, 0x03, curr_val);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 0, 1);
|
|
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 1, 1);
|
|
+static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 2, 1);
|
|
+static DEVICE_ATTR(temp1_curr, S_IRUGO, show_temp1_curr_temp, NULL);
|
|
+
|
|
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 0, 2);
|
|
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 1, 2);
|
|
+static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 2, 2);
|
|
+static DEVICE_ATTR(temp2_curr, S_IRUGO, show_temp2_curr_temp, NULL);
|
|
+
|
|
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 0, 4);
|
|
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 1, 4);
|
|
+static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR,
|
|
+ show_temp_auto_offset, store_temp_auto_offset, 2, 4);
|
|
+static DEVICE_ATTR(temp3_curr, S_IRUGO, show_temp3_curr_temp, NULL);
|
|
+
|
|
+static DEVICE_ATTR(hyster, S_IRUGO | S_IWUSR, show_temp_hyst, store_temp_hyst);
|
|
+static DEVICE_ATTR(status, S_IRUGO, show_status_reg, NULL);
|
|
+
|
|
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
|
|
+ show_power_state, store_power_state);
|
|
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, show_mode, store_mode);
|
|
+
|
|
+static struct attribute *mid_att_thermal[] = {
|
|
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
|
|
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
|
|
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
|
|
+ &dev_attr_temp1_curr.attr,
|
|
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
|
|
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
|
|
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
|
|
+ &dev_attr_temp2_curr.attr,
|
|
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
|
|
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
|
|
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
|
|
+ &dev_attr_temp3_curr.attr,
|
|
+ &dev_attr_hyster.attr,
|
|
+ &dev_attr_status.attr,
|
|
+ &dev_attr_power_state.attr,
|
|
+ &dev_attr_mode.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group m_thermal_gr = {
|
|
+ .name = "emc1403",
|
|
+ .attrs = mid_att_thermal
|
|
+};
|
|
+
|
|
+static void emc1403_set_default_config(struct i2c_client *client)
|
|
+{
|
|
+ i2c_smbus_write_byte_data(client, 0x03, 0x00);
|
|
+ i2c_smbus_write_byte_data(client, 0x04, 0x02);
|
|
+ i2c_smbus_write_byte_data(client, 0x22, 0x00);
|
|
+}
|
|
+
|
|
+#ifdef MOORESTOWN_INTERRUPT_ENABLE
|
|
+static irqreturn_t therm_interrupt_handler(int id, void *dev)
|
|
+{
|
|
+ struct thermal_data *data = (struct thermal_data *)dev;
|
|
+ schedule_work(&data->therm_handler);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static irqreturn_t alert_interrupt_handler(int id, void *dev)
|
|
+{
|
|
+ struct thermal_data *data = (struct thermal_data *)dev;
|
|
+ schedule_work(&data->alert_handler);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+/* when the device raise the interrupt we mask the interrupt
|
|
+ * bit for that device as the status register is R-C
|
|
+ * so that till thermal governor doesnot take action we need
|
|
+ * not to send continuous events */
|
|
+
|
|
+static int interrupt_status(struct i2c_client *client, u8 diode_reg_val,
|
|
+ u8 *status, u8 event)
|
|
+{
|
|
+ u8 crit_st = 0, set_mask = 0;
|
|
+
|
|
+ set_mask = i2c_read_current_data(client, 0x1F);
|
|
+ if (diode_reg_val & THM_CHAN_TEMP[3]) {
|
|
+ set_mask = (set_mask | 0x02);
|
|
+ crit_st = (crit_st | 2);
|
|
+ }
|
|
+ if (diode_reg_val & THM_CHAN_TEMP[2]) {
|
|
+ set_mask = (set_mask | 0x04);
|
|
+ crit_st = (crit_st | 4);
|
|
+ }
|
|
+ if (diode_reg_val & THM_CHAN_TEMP[4]) {
|
|
+ set_mask = (set_mask | 0x01);
|
|
+ crit_st = (crit_st | 1);
|
|
+ }
|
|
+ if (event == ALERT_EVENT)
|
|
+ i2c_smbus_write_byte_data(client, 0x1F, set_mask);
|
|
+ *status = crit_st;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ospm_event(int event_id, int sensor_id, int curr_temp)
|
|
+{
|
|
+ if (event_id == THERM_EVENT) {
|
|
+ printk(KERN_ALERT "emc1403: Sensor Id = %d crit event \
|
|
+ temp = %d \n", sensor_id, curr_temp);
|
|
+ ospm_generate_netlink_event(sensor_id,
|
|
+ OSPM_EVENT_THERMAL_CRITICAL);
|
|
+ }
|
|
+ if (event_id == HIGH_EVENT) {
|
|
+ printk(KERN_ALERT "emc1403: Sensor Id = %d AUX1 event \
|
|
+ temp = %d \n", sensor_id, curr_temp);
|
|
+ ospm_generate_netlink_event(sensor_id,
|
|
+ OSPM_EVENT_THERMAL_AUX1);
|
|
+ }
|
|
+ if (event_id == LOW_EVENT) {
|
|
+ printk(KERN_ALERT "emc1403: Sensor Id = %d AUX0 event \
|
|
+ temp = %d \n", sensor_id, curr_temp);
|
|
+ ospm_generate_netlink_event(sensor_id,
|
|
+ OSPM_EVENT_THERMAL_AUX0);
|
|
+ }
|
|
+ if (event_id == FAULT_EVENT) {
|
|
+ printk(KERN_ALERT "emc1403: Sensor Id = %d Fault event \
|
|
+ temp = %d \n", sensor_id, curr_temp);
|
|
+ ospm_generate_netlink_event(sensor_id,
|
|
+ OSPM_EVENT_THERMAL_DEV_FAULT);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void send_event(struct i2c_client *client, int status, int event_id)
|
|
+{
|
|
+ int ret_val;
|
|
+
|
|
+ if (status & TEMP1) {
|
|
+ ret_val = i2c_read_current_data(client, THM_REG_CURR_TEMP[0]);
|
|
+ ospm_event(event_id, TEMP_DEV_ID1, ret_val);
|
|
+ }
|
|
+ if (status & TEMP2) {
|
|
+ ret_val = i2c_read_current_data(client, THM_REG_CURR_TEMP[1]);
|
|
+ ospm_event(event_id, TEMP_DEV_ID2, ret_val);
|
|
+ }
|
|
+ if (status & TEMP3) {
|
|
+ ret_val = i2c_read_current_data(client, THM_REG_CURR_TEMP[2]);
|
|
+ ospm_event(event_id, TEMP_DEV_ID3, ret_val);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void therm_handle_intrpt(struct work_struct *work)
|
|
+{
|
|
+ u8 status, reg_val;
|
|
+ struct thermal_data *data = container_of(work,
|
|
+ struct thermal_data, therm_handler);
|
|
+
|
|
+ /* check if therm_module_info is initialized */
|
|
+ if (!data)
|
|
+ return;
|
|
+ /* Which DIODE has raised the interrupt 0x1B
|
|
+ internal/External1/External2 */
|
|
+ reg_val = i2c_smbus_read_byte_data(data->client,
|
|
+ THM_STAT_REG_TEMP[0]);
|
|
+ interrupt_status(data->client, reg_val, &status, THERM_EVENT);
|
|
+ send_event(data->client, status, THERM_EVENT);
|
|
+}
|
|
+
|
|
+static void alert_handle_intrpt(struct work_struct *work)
|
|
+{
|
|
+ int sta_reg_val, reg_val;
|
|
+ u8 status;
|
|
+ struct thermal_data *data = container_of(work,
|
|
+ struct thermal_data, alert_handler);
|
|
+ if (!data)
|
|
+ return;
|
|
+ /* HIGH/ LOW / FAULT Alert has occured for */
|
|
+ reg_val = i2c_smbus_read_byte_data(data->client, THM_STAT_REG_TEMP[4]);
|
|
+ /* High status bit is set */
|
|
+ if (reg_val & THM_CHAN_TEMP[0]) {
|
|
+ /* Which DIODE has raised the interrupt 0x1B
|
|
+ internal/External1/External2 */
|
|
+ sta_reg_val = i2c_smbus_read_byte_data(data->client,
|
|
+ THM_STAT_REG_TEMP[1]);
|
|
+ interrupt_status(data->client, sta_reg_val, &status,
|
|
+ ALERT_EVENT);
|
|
+ send_event(data->client, status, HIGH_EVENT);
|
|
+ }
|
|
+ /* Low status bit is set */
|
|
+ if (reg_val & THM_CHAN_TEMP[1]) {
|
|
+ sta_reg_val = i2c_smbus_read_byte_data(data->client,
|
|
+ THM_STAT_REG_TEMP[2]);
|
|
+ interrupt_status(data->client, sta_reg_val, &status,
|
|
+ ALERT_EVENT);
|
|
+ send_event(data->client, status, LOW_EVENT);
|
|
+ }
|
|
+ /* Fault status bit is set */
|
|
+ if (reg_val & THM_CHAN_TEMP[2]) {
|
|
+ sta_reg_val = i2c_smbus_read_byte_data(data->client,
|
|
+ THM_STAT_REG_TEMP[3]);
|
|
+ interrupt_status(data->client, sta_reg_val, &status,
|
|
+ ALERT_EVENT);
|
|
+ send_event(data->client, status, FAULT_EVENT);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int emc1403_probe(struct i2c_client *new_client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ int res = 0;
|
|
+ struct thermal_data *data;
|
|
+ u16 pid, smsc_id, revision;
|
|
+
|
|
+#ifdef MOORESTOWN_INTERRUPT_ENABLE
|
|
+ u16 t_irq, a_irq;
|
|
+#endif
|
|
+ data = kzalloc(sizeof(struct thermal_data), GFP_KERNEL);
|
|
+
|
|
+ if (data == NULL) {
|
|
+ printk(KERN_WARNING "emc1403: Memory allocation failed");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ data->client = new_client;
|
|
+ i2c_set_clientdata(new_client, data);
|
|
+
|
|
+ /* Check if thermal chip is SMSC and EMC1403 */
|
|
+ smsc_id = i2c_read_current_data(new_client,
|
|
+ THERMAL_SMSC_ID_REG);
|
|
+ if (smsc_id != 0x5d) {
|
|
+ printk(KERN_WARNING "emc1403: vendor id mismatch \n");
|
|
+ goto thermal_error1;
|
|
+ }
|
|
+ pid = i2c_read_current_data(new_client, THERMAL_PID_REG);
|
|
+ if (pid != 0x21) {
|
|
+ printk(KERN_WARNING "emc1403: Prod id mismatch \n");
|
|
+ goto thermal_error1;
|
|
+ }
|
|
+ revision = i2c_read_current_data(new_client,
|
|
+ THERMAL_REVISION_REG);
|
|
+ if (revision != 0x01) {
|
|
+ printk(KERN_WARNING "emc1403: Rev id mismatch is \n");
|
|
+ goto thermal_error1;
|
|
+ }
|
|
+ res = sysfs_create_group(&new_client->dev.kobj, &m_thermal_gr);
|
|
+ if (res) {
|
|
+ printk(KERN_WARNING "emc1403: create group failed! \n");
|
|
+ hwmon_device_unregister(data->hwmon_dev);
|
|
+ goto thermal_error1;
|
|
+ }
|
|
+ data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
|
+ if (IS_ERR(data->hwmon_dev)) {
|
|
+ res = PTR_ERR(data->hwmon_dev);
|
|
+ data->hwmon_dev = NULL;
|
|
+ printk(KERN_WARNING "emc1403:Register hwmon dev Failed\n");
|
|
+ goto thermal_error1;
|
|
+ }
|
|
+#ifdef MOORESTOWN_INTERRUPT_ENABLE
|
|
+ INIT_WORK(&data->therm_handler, (void *)therm_handle_intrpt);
|
|
+ INIT_WORK(&data->alert_handler, (void *)alert_handle_intrpt);
|
|
+ t_irq = new_client->irq;
|
|
+ a_irq = *(short *)new_client->dev.platform_data;
|
|
+ data->therm_irq = t_irq & ~IRQ_TYPE_MASK;
|
|
+ data->alert_irq = a_irq & ~IRQ_TYPE_MASK;
|
|
+ /* interpret irq field */
|
|
+ if (data->therm_irq == 0x113) {
|
|
+ if (t_irq & IRQ_TYPE_MASK) {
|
|
+ /* irq -> GPE_ID */
|
|
+ res = request_gpe(data->therm_irq,
|
|
+ (gpio_function_t)therm_interrupt_handler,
|
|
+ data, DETECT_LEVEL_LOW);
|
|
+ if (res)
|
|
+ dev_crit(&new_client->dev, "%s(): cannot \
|
|
+ register therm gpe \n", __func__);
|
|
+ } else {
|
|
+ res = request_irq(data->therm_irq,
|
|
+ therm_interrupt_handler,
|
|
+ DETECT_LEVEL_LOW, "emc1403", data);
|
|
+ if (res)
|
|
+ dev_crit(&new_client->dev, "%s(): \
|
|
+ cannot get therm IRQ\n", __func__);
|
|
+ }
|
|
+ } else {
|
|
+ printk(KERN_WARNING"emc1403: IRQ mismatch \
|
|
+ sent for therm registration");
|
|
+ }
|
|
+ if (data->alert_irq == 0x114) {
|
|
+ if (a_irq & IRQ_TYPE_MASK) {
|
|
+ /* irq -> GPE_ID */
|
|
+ res = request_gpe(data->alert_irq,
|
|
+ (gpio_function_t)alert_interrupt_handler,
|
|
+ data, DETECT_LEVEL_LOW);
|
|
+ if (res)
|
|
+ dev_crit(&new_client->dev, "%s(): \
|
|
+ cannot register alert gpe \n", __func__);
|
|
+ } else {
|
|
+ res = request_irq(data->alert_irq,
|
|
+ alert_interrupt_handler, DETECT_LEVEL_LOW,
|
|
+ "emc1403", data);
|
|
+ if (res)
|
|
+ dev_crit(&new_client->dev, "%s(): cannot \
|
|
+ get alert IRQ\n", __func__);
|
|
+ }
|
|
+ } else {
|
|
+ printk(KERN_WARNING"emc1403: IRQ mismatch \
|
|
+ sent for alert registration");
|
|
+ }
|
|
+#endif
|
|
+ emc1403_set_default_config(new_client);
|
|
+ dev_info(&new_client->dev, "%s EMC1403 Thermal chip found \n",
|
|
+ new_client->name);
|
|
+ return res;
|
|
+thermal_error1:
|
|
+ i2c_set_clientdata(new_client, NULL);
|
|
+ kfree(data);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int emc1403_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct thermal_data *data = i2c_get_clientdata(client);
|
|
+
|
|
+ hwmon_device_unregister(data->hwmon_dev);
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
|
|
+ kfree(data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct i2c_device_id emc1403_idtable[] = {
|
|
+ { "i2c_thermal", 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
|
|
+
|
|
+static struct i2c_driver sensor_emc1403 = {
|
|
+ .driver = {
|
|
+ .name = "emc1403",
|
|
+ },
|
|
+ .probe = emc1403_probe,
|
|
+ .remove = emc1403_remove,
|
|
+ .id_table = emc1403_idtable,
|
|
+};
|
|
+
|
|
+static int __init sensor_emc1403_init(void)
|
|
+{
|
|
+ return i2c_add_driver(&sensor_emc1403);
|
|
+}
|
|
+
|
|
+static void __exit sensor_emc1403_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&sensor_emc1403);
|
|
+}
|
|
+
|
|
+module_init(sensor_emc1403_init);
|
|
+module_exit(sensor_emc1403_exit);
|
|
--- /dev/null
|
|
+++ b/drivers/hwmon/hmc6352.c
|
|
@@ -0,0 +1,250 @@
|
|
+/*
|
|
+ * hmc6352.c - Honeywell Compass Driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Intel Corp
|
|
+ *
|
|
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+ *
|
|
+ * 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; version 2 of the License.
|
|
+ *
|
|
+ * 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/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/hwmon.h>
|
|
+#include <linux/hwmon-sysfs.h>
|
|
+#include <linux/hwmon-vid.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <asm/ipc_defs.h>
|
|
+
|
|
+
|
|
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
|
|
+MODULE_DESCRIPTION("hmc6352 Compass Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|
|
+/* internal return values */
|
|
+#define COMP_CALIB_START 1
|
|
+#define COMP_CALIB_STOP 2
|
|
+#define COMP_SLEEP_MODE 0
|
|
+#define COMP_ACTIVE_MODE 1
|
|
+
|
|
+struct compass_data {
|
|
+ struct device *hwmon_dev;
|
|
+};
|
|
+
|
|
+static ssize_t compass_calibration_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret;
|
|
+ unsigned long val;
|
|
+ char cmd[] = {0x43};
|
|
+ char cmd1[] = {0x45};
|
|
+ struct i2c_msg msg[] = {
|
|
+ { client->addr, 0, 1, cmd },
|
|
+ };
|
|
+ struct i2c_msg msg1[] = {
|
|
+ { client->addr, 0, 1, cmd1 },
|
|
+ };
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ if (val == COMP_CALIB_START) {
|
|
+ client->addr = 0x21;
|
|
+ ret = i2c_transfer(client->adapter, msg, 1);
|
|
+ if (ret != 1) {
|
|
+ printk(KERN_WARNING "hmc6352_comp : i2c callib start \
|
|
+ cmd failed \n");
|
|
+ return ret;
|
|
+ }
|
|
+ } else if (val == COMP_CALIB_STOP) {
|
|
+ client->addr = 0x21;
|
|
+ ret = i2c_transfer(client->adapter, msg1, 1);
|
|
+ if (ret != 1) {
|
|
+ printk(KERN_WARNING " hmc6352_comp : i2c callib stop \
|
|
+ cmd failed \n");
|
|
+ return ret;
|
|
+ }
|
|
+ } else
|
|
+ return -EINVAL;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t compass_heading_data_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ char cmd[] = {0x41};
|
|
+ unsigned char i2c_data[2] = {0, 0};
|
|
+ unsigned int ret, ret_val;
|
|
+ struct i2c_msg msg[] = {
|
|
+ { client->addr, 0, 1, cmd },
|
|
+ };
|
|
+ struct i2c_msg msg1[] = {
|
|
+ { client->addr, I2C_M_RD, 2, i2c_data },
|
|
+ };
|
|
+
|
|
+ client->addr = 0x21;
|
|
+ ret = i2c_transfer(client->adapter, msg, 1);
|
|
+ if (ret != 1) {
|
|
+ printk(KERN_WARNING "hmc6352 : i2c cmd 0x41 failed \n");
|
|
+ return ret;
|
|
+ }
|
|
+ msleep(10); /* sending 0x41 cmd we need to wait for 7-10 milli second*/
|
|
+ ret = i2c_transfer(client->adapter, msg1, 1);
|
|
+ if (ret != 1) {
|
|
+ printk(KERN_WARNING "hmc6352 : i2c read data cmd failed \n");
|
|
+ return ret;
|
|
+ }
|
|
+ ret_val = i2c_data[0];
|
|
+ ret_val = ((ret_val << 8) | i2c_data[1]);
|
|
+ return sprintf(buf, "%d.%d\n", ret_val/10, ret_val%10);
|
|
+}
|
|
+
|
|
+static ssize_t compass_power_mode_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned long val;
|
|
+ unsigned int ret;
|
|
+ char cmd[] = {0x53};
|
|
+ char cmd1[] = {0x57};
|
|
+ struct i2c_msg msg[] = {
|
|
+ { client->addr, 0, 1, cmd },
|
|
+ };
|
|
+ struct i2c_msg msg1[] = {
|
|
+ { client->addr, 0, 1, cmd1 },
|
|
+ };
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (val == COMP_SLEEP_MODE) {
|
|
+ ret = i2c_transfer(client->adapter, msg, 1);
|
|
+ if (ret != 1)
|
|
+ printk(KERN_WARNING "hmc6352: i2c cmd sleep mode \
|
|
+ failed \n");
|
|
+ } else if (val == COMP_ACTIVE_MODE) {
|
|
+ ret = i2c_transfer(client->adapter, msg1, 1);
|
|
+ if (ret != 1)
|
|
+ printk(KERN_WARNING "hmc6352: i2c cmd active mode \
|
|
+ failed \n");
|
|
+ } else
|
|
+ return -EINVAL;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(heading, S_IRUGO, compass_heading_data_show, NULL);
|
|
+static DEVICE_ATTR(calibration, S_IWUSR, NULL, compass_calibration_store);
|
|
+static DEVICE_ATTR(power_state, S_IWUSR, NULL, compass_power_mode_store);
|
|
+
|
|
+static struct attribute *mid_att_compass[] = {
|
|
+ &dev_attr_heading.attr,
|
|
+ &dev_attr_calibration.attr,
|
|
+ &dev_attr_power_state.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group m_compass_gr = {
|
|
+ .name = "hmc6352",
|
|
+ .attrs = mid_att_compass
|
|
+};
|
|
+
|
|
+static int hmc6352_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ int res;
|
|
+ struct compass_data *data;
|
|
+
|
|
+ data = kzalloc(sizeof(struct compass_data), GFP_KERNEL);
|
|
+ if (data == NULL) {
|
|
+ printk(KERN_WARNING "hmc6352: Memory initialization failed");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ i2c_set_clientdata(client, data);
|
|
+
|
|
+ res = sysfs_create_group(&client->dev.kobj, &m_compass_gr);
|
|
+ if (res) {
|
|
+ printk(KERN_WARNING "hmc6352: device_create_file failed!!\n");
|
|
+ goto compass_error1;
|
|
+ }
|
|
+ data->hwmon_dev = hwmon_device_register(&client->dev);
|
|
+ if (IS_ERR(data->hwmon_dev)) {
|
|
+ res = PTR_ERR(data->hwmon_dev);
|
|
+ data->hwmon_dev = NULL;
|
|
+ printk(KERN_WARNING "hmc6352: fail to register hwmon device\n");
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_compass_gr);
|
|
+ goto compass_error1;
|
|
+ }
|
|
+ dev_info(&client->dev, "%s HMC6352 compass chip found \n",
|
|
+ client->name);
|
|
+ return res;
|
|
+
|
|
+compass_error1:
|
|
+ i2c_set_clientdata(client, NULL);
|
|
+ kfree(data);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int hmc6352_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct compass_data *data = i2c_get_clientdata(client);
|
|
+
|
|
+ hwmon_device_unregister(data->hwmon_dev);
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_compass_gr);
|
|
+ kfree(data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct i2c_device_id hmc6352_id[] = {
|
|
+ { "i2c_compass", 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, hmc6352_id);
|
|
+
|
|
+static struct i2c_driver hmc6352_driver = {
|
|
+ .driver = {
|
|
+ .name = "hmc6352",
|
|
+ },
|
|
+ .probe = hmc6352_probe,
|
|
+ .remove = hmc6352_remove,
|
|
+ .id_table = hmc6352_id,
|
|
+};
|
|
+
|
|
+static int __init sensor_hmc6352_init(void)
|
|
+{
|
|
+ int res;
|
|
+
|
|
+ res = i2c_add_driver(&hmc6352_driver);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static void __exit sensor_hmc6352_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&hmc6352_driver);
|
|
+}
|
|
+
|
|
+module_init(sensor_hmc6352_init);
|
|
+module_exit(sensor_hmc6352_exit);
|
|
--- /dev/null
|
|
+++ b/drivers/hwmon/isl29020.c
|
|
@@ -0,0 +1,248 @@
|
|
+/*
|
|
+ * isl29020.c - Intersil ALS Driver
|
|
+ *
|
|
+ * Copyright (C) 2008 Intel Corp
|
|
+ *
|
|
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+ *
|
|
+ * 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; version 2 of the License.
|
|
+ *
|
|
+ * 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/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/hwmon.h>
|
|
+#include <linux/hwmon-sysfs.h>
|
|
+#include <linux/hwmon-vid.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <asm/ipc_defs.h>
|
|
+
|
|
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
|
|
+MODULE_DESCRIPTION("intersil isl29020 ALS Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|
|
+#define ALS_MIN_RANGE_VAL 0
|
|
+#define ALS_MAX_RANGE_VAL 5
|
|
+#define POWER_STA_ENABLE 1
|
|
+#define POWER_STA_DISABLE 0
|
|
+
|
|
+struct als_data {
|
|
+ struct device *hwmon_dev;
|
|
+};
|
|
+
|
|
+static unsigned int i2c_write_current_data(struct i2c_client *client,
|
|
+ unsigned int reg, unsigned int value)
|
|
+{
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_write_byte_data(client, reg, value);
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+static ssize_t als_sensing_range_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int val;
|
|
+
|
|
+ val = i2c_smbus_read_byte_data(client, 0x00);
|
|
+ return sprintf(buf, "%d000\n", 1 << (2 * (val & 3)));
|
|
+
|
|
+}
|
|
+
|
|
+static ssize_t als_lux_output_data_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned int ret_val, val;
|
|
+ unsigned long int lux, max_count;
|
|
+ int tempv1, tempv2;
|
|
+
|
|
+ max_count = 65535;
|
|
+ tempv1 = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */
|
|
+ tempv2 = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */
|
|
+ ret_val = tempv1;
|
|
+ ret_val = (ret_val << 8 | tempv2);
|
|
+ val = i2c_smbus_read_byte_data(client, 0x00);
|
|
+ lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / max_count;
|
|
+ return sprintf(buf, "%ld\n", lux);
|
|
+}
|
|
+
|
|
+static ssize_t als_sensing_range_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned int ret_val, set_val = 0;
|
|
+ unsigned long val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x00);
|
|
+ ret_val = ret_val & 0xFC; /*reset the bit before setting them */
|
|
+ if (val == 1)
|
|
+ set_val = (ret_val | 0x00); /* setting the 1:0 bit */
|
|
+ else if (val == 2)
|
|
+ set_val = (ret_val | 0x01);
|
|
+ else if (val == 3)
|
|
+ set_val = (ret_val | 0x02);
|
|
+ else if (val == 4)
|
|
+ set_val = (ret_val | 0x03);
|
|
+ else
|
|
+ goto invarg;
|
|
+ i2c_write_current_data(client, 0x00, set_val);
|
|
+ return count;
|
|
+invarg:
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static ssize_t als_power_status_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x00);
|
|
+ ret_val = ret_val & 0x80;
|
|
+ if (ret_val == 0x80)
|
|
+ ret_val = 1;
|
|
+ return sprintf(buf, "%x", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t als_power_status_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ unsigned long val = 0;
|
|
+ char curr_val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+
|
|
+ curr_val = i2c_smbus_read_byte_data(client, 0x00);
|
|
+ if (val == POWER_STA_ENABLE)
|
|
+ curr_val = curr_val | 0x80;
|
|
+ else if (val == POWER_STA_DISABLE)
|
|
+ curr_val = curr_val & 0x7F;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+ i2c_write_current_data(client, 0x00, curr_val);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(sensing_range, S_IRUGO | S_IWUSR,
|
|
+ als_sensing_range_show, als_sensing_range_store);
|
|
+static DEVICE_ATTR(lux_output, S_IRUGO, als_lux_output_data_show, NULL);
|
|
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
|
|
+ als_power_status_show, als_power_status_store);
|
|
+
|
|
+static struct attribute *mid_att_als[] = {
|
|
+ &dev_attr_sensing_range.attr,
|
|
+ &dev_attr_lux_output.attr,
|
|
+ &dev_attr_power_state.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group m_als_gr = {
|
|
+ .name = "isl29020",
|
|
+ .attrs = mid_att_als
|
|
+};
|
|
+
|
|
+static void als_set_default_config(struct i2c_client *client)
|
|
+{
|
|
+ i2c_write_current_data(client, 0x00, 0xc0);
|
|
+}
|
|
+
|
|
+static int isl29020_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ int res;
|
|
+ struct als_data *data;
|
|
+
|
|
+ data = kzalloc(sizeof(struct als_data), GFP_KERNEL);
|
|
+ if (data == NULL) {
|
|
+ printk(KERN_WARNING " isl29020: Memory initialization failed");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ i2c_set_clientdata(client, data);
|
|
+
|
|
+ res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
|
|
+ if (res) {
|
|
+ printk(KERN_WARNING "isl29020: device create file failed!!\n");
|
|
+ goto als_error1;
|
|
+ }
|
|
+ data->hwmon_dev = hwmon_device_register(&client->dev);
|
|
+ if (IS_ERR(data->hwmon_dev)) {
|
|
+ res = PTR_ERR(data->hwmon_dev);
|
|
+ data->hwmon_dev = NULL;
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_als_gr);
|
|
+ printk(KERN_ALERT "isl29020:unable to register hwmon device\n");
|
|
+ goto als_error1;
|
|
+ }
|
|
+ dev_info(&client->dev, "%s isl29020: ALS chip found \n", client->name);
|
|
+ als_set_default_config(client);
|
|
+ return res;
|
|
+
|
|
+als_error1:
|
|
+ i2c_set_clientdata(client, NULL);
|
|
+ kfree(data);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int isl29020_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct als_data *data = i2c_get_clientdata(client);
|
|
+
|
|
+ hwmon_device_unregister(data->hwmon_dev);
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_als_gr);
|
|
+ kfree(data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct i2c_device_id isl29020_id[] = {
|
|
+ { "i2c_als", 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, isl29020_id);
|
|
+
|
|
+static struct i2c_driver isl29020_driver = {
|
|
+ .driver = {
|
|
+ .name = "isl29020",
|
|
+ },
|
|
+ .probe = isl29020_probe,
|
|
+ .remove = isl29020_remove,
|
|
+ .id_table = isl29020_id,
|
|
+};
|
|
+
|
|
+static int __init sensor_isl29020_init(void)
|
|
+{
|
|
+ int res;
|
|
+
|
|
+ res = i2c_add_driver(&isl29020_driver);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static void __exit sensor_isl29020_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&isl29020_driver);
|
|
+}
|
|
+
|
|
+module_init(sensor_isl29020_init);
|
|
+module_exit(sensor_isl29020_exit);
|
|
--- /dev/null
|
|
+++ b/drivers/hwmon/lis331dl.c
|
|
@@ -0,0 +1,322 @@
|
|
+/*
|
|
+ * lis331dl.c - ST LIS331DL Accelerometer Driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Intel Corp
|
|
+ *
|
|
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+ *
|
|
+ * 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; version 2 of the License.
|
|
+ *
|
|
+ * 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/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/hwmon.h>
|
|
+#include <linux/hwmon-sysfs.h>
|
|
+#include <linux/hwmon-vid.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/sysfs.h>
|
|
+
|
|
+
|
|
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
|
|
+MODULE_DESCRIPTION("STMacroelectronics LIS331DL Accelerometer Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|
|
+#define ACCEL_DATA_RATE_100HZ 0
|
|
+#define ACCEL_DATA_RATE_400HZ 1
|
|
+#define ACCEL_POWER_MODE_DOWN 0
|
|
+#define ACCEL_POWER_MODE_ACTIVE 1
|
|
+#define ACCEL_NORMAL_MODE 0
|
|
+#define ACCEL_MEMORY_REBOOT 1
|
|
+
|
|
+/* internal return values */
|
|
+
|
|
+struct acclero_data {
|
|
+ struct device *hwmon_dev;
|
|
+ struct mutex update_lock;
|
|
+};
|
|
+
|
|
+static unsigned int i2c_write_current_data(struct i2c_client *client,
|
|
+ unsigned int reg, unsigned int value)
|
|
+{
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_write_byte_data(client, reg, value);
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+static ssize_t data_rate_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val, val;
|
|
+
|
|
+ val = i2c_smbus_read_byte_data(client, 0x20);
|
|
+ ret_val = (val & 0x80); /* 1= 400HZ 0= 100HZ */
|
|
+ if (ret_val == 0x80)
|
|
+ ret_val = 1;
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+
|
|
+}
|
|
+
|
|
+static ssize_t power_mode_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val, val;
|
|
+
|
|
+ val = i2c_smbus_read_byte_data(client, 0x20);
|
|
+ ret_val = (val & 0x40);
|
|
+ if (ret_val == 0x40)
|
|
+ ret_val = 1;
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t x_pos_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x29);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t y_pos_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x2B);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t z_pos_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ int ret_val;
|
|
+
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x2D);
|
|
+ return sprintf(buf, "%d\n", ret_val);
|
|
+}
|
|
+
|
|
+static ssize_t xyz_pos_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ int x, y, z;
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+
|
|
+ x = i2c_smbus_read_byte_data(client, 0x29);
|
|
+ y = i2c_smbus_read_byte_data(client, 0x2B);
|
|
+ z = i2c_smbus_read_byte_data(client, 0x2D);
|
|
+ return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
|
|
+}
|
|
+
|
|
+static ssize_t data_rate_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct acclero_data *data = i2c_get_clientdata(client);
|
|
+ unsigned int ret_val, set_val;
|
|
+ unsigned long val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x20);
|
|
+
|
|
+ mutex_lock(&data->update_lock);
|
|
+ if (val == ACCEL_DATA_RATE_100HZ)
|
|
+ set_val = (ret_val & 0x7F); /* setting the 8th bit to 0 */
|
|
+ else if (val == ACCEL_DATA_RATE_400HZ)
|
|
+ set_val = (ret_val | (1 << 7));
|
|
+ else
|
|
+ goto invarg;
|
|
+
|
|
+ i2c_write_current_data(client, 0x20, set_val);
|
|
+ mutex_unlock(&data->update_lock);
|
|
+ return count;
|
|
+invarg:
|
|
+ mutex_unlock(&data->update_lock);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static ssize_t power_mode_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct acclero_data *data = i2c_get_clientdata(client);
|
|
+ unsigned int ret_val, set_val;
|
|
+ unsigned long val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x20);
|
|
+
|
|
+ mutex_lock(&data->update_lock);
|
|
+ if (val == ACCEL_POWER_MODE_DOWN)
|
|
+ set_val = ret_val & 0xBF; /* if value id 0 */
|
|
+ else if (val == ACCEL_POWER_MODE_ACTIVE)
|
|
+ set_val = (ret_val | (1<<6)); /* if value is 1 */
|
|
+ else
|
|
+ goto invarg;
|
|
+
|
|
+ i2c_write_current_data(client, 0x20, set_val);
|
|
+ mutex_unlock(&data->update_lock);
|
|
+ return count;
|
|
+invarg:
|
|
+ mutex_unlock(&data->update_lock);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static ssize_t reboot_mem_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct acclero_data *data = i2c_get_clientdata(client);
|
|
+ unsigned int ret_val, set_val;
|
|
+ unsigned long val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ ret_val = i2c_smbus_read_byte_data(client, 0x21);
|
|
+ if (val == ACCEL_MEMORY_REBOOT) {
|
|
+ mutex_lock(&data->update_lock);
|
|
+ set_val = (ret_val | (1 << 6)); /* setting the 6th bit */
|
|
+ i2c_write_current_data(client, 0x21, set_val);
|
|
+ mutex_unlock(&data->update_lock);
|
|
+ } else
|
|
+ return -EINVAL;
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(data_rate, S_IRUGO | S_IWUSR,
|
|
+ data_rate_show, data_rate_store);
|
|
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
|
|
+ power_mode_show, power_mode_store);
|
|
+static DEVICE_ATTR(reboot_mem, S_IWUSR, NULL,
|
|
+ reboot_mem_store);
|
|
+static DEVICE_ATTR(x, S_IRUGO, x_pos_show, NULL);
|
|
+static DEVICE_ATTR(y, S_IRUGO, y_pos_show, NULL);
|
|
+static DEVICE_ATTR(z, S_IRUGO, z_pos_show, NULL);
|
|
+static DEVICE_ATTR(curr_pos, S_IRUGO, xyz_pos_show, NULL);
|
|
+
|
|
+static struct attribute *mid_att_acclero[] = {
|
|
+ &dev_attr_data_rate.attr,
|
|
+ &dev_attr_power_state.attr,
|
|
+ &dev_attr_reboot_mem.attr,
|
|
+ &dev_attr_x.attr,
|
|
+ &dev_attr_y.attr,
|
|
+ &dev_attr_z.attr,
|
|
+ &dev_attr_curr_pos.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group m_acclero_gr = {
|
|
+ .name = "lis331dl",
|
|
+ .attrs = mid_att_acclero
|
|
+};
|
|
+
|
|
+static void accel_set_default_config(struct i2c_client *client)
|
|
+{
|
|
+ i2c_write_current_data(client, 0x20, 0x47);
|
|
+}
|
|
+
|
|
+static int lis331dl_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ int res;
|
|
+ struct acclero_data *data;
|
|
+
|
|
+ data = kzalloc(sizeof(struct acclero_data), GFP_KERNEL);
|
|
+ if (data == NULL) {
|
|
+ printk(KERN_WARNING "lis331dl: Memory initi failed \n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ mutex_init(&data->update_lock);
|
|
+ i2c_set_clientdata(client, data);
|
|
+
|
|
+ res = sysfs_create_group(&client->dev.kobj, &m_acclero_gr);
|
|
+ if (res) {
|
|
+ printk(KERN_WARNING "lis331dl: Sysfs group failed!!\n");
|
|
+ goto acclero_error1;
|
|
+ }
|
|
+ data->hwmon_dev = hwmon_device_register(&client->dev);
|
|
+ if (IS_ERR(data->hwmon_dev)) {
|
|
+ res = PTR_ERR(data->hwmon_dev);
|
|
+ data->hwmon_dev = NULL;
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_acclero_gr);
|
|
+ printk(KERN_WARNING "lis331dl: unable to register \
|
|
+ hwmon device\n");
|
|
+ goto acclero_error1;
|
|
+ }
|
|
+ accel_set_default_config(client);
|
|
+
|
|
+ dev_info(&client->dev, "%s lis331dl: Accelerometer chip \
|
|
+ foundn", client->name);
|
|
+ return res;
|
|
+
|
|
+acclero_error1:
|
|
+ i2c_set_clientdata(client, NULL);
|
|
+ kfree(data);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int lis331dl_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct acclero_data *data = i2c_get_clientdata(client);
|
|
+
|
|
+ hwmon_device_unregister(data->hwmon_dev);
|
|
+ sysfs_remove_group(&client->dev.kobj, &m_acclero_gr);
|
|
+ kfree(data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct i2c_device_id lis331dl_id[] = {
|
|
+ { "i2c_accel", 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, lis331dl_id);
|
|
+
|
|
+static struct i2c_driver lis331dl_driver = {
|
|
+ .driver = {
|
|
+ .name = "lis331dl",
|
|
+ },
|
|
+ .probe = lis331dl_probe,
|
|
+ .remove = lis331dl_remove,
|
|
+ .id_table = lis331dl_id,
|
|
+};
|
|
+
|
|
+static int __init sensor_lis331dl_init(void)
|
|
+{
|
|
+ int res;
|
|
+
|
|
+ res = i2c_add_driver(&lis331dl_driver);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static void __exit sensor_lis331dl_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&lis331dl_driver);
|
|
+}
|
|
+
|
|
+module_init(sensor_lis331dl_init);
|
|
+module_exit(sensor_lis331dl_exit);
|
|
--- a/drivers/misc/Kconfig
|
|
+++ b/drivers/misc/Kconfig
|
|
@@ -249,6 +249,13 @@ config SGI_GRU_DEBUG
|
|
This option enables addition debugging code for the SGI GRU driver. If
|
|
you are unsure, say N.
|
|
|
|
+config MRST_VIB
|
|
+ tristate "vibrator driver for Intel Moorestown platform"
|
|
+ help
|
|
+ Vibrator for Intel Moorestown platform.
|
|
+
|
|
+ If unsure, say N.
|
|
+
|
|
config ISL29003
|
|
tristate "Intersil ISL29003 ambient light sensor"
|
|
depends on I2C && SYSFS
|
|
--- a/drivers/misc/Makefile
|
|
+++ b/drivers/misc/Makefile
|
|
@@ -22,6 +22,7 @@ obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfg
|
|
obj-$(CONFIG_HP_ILO) += hpilo.o
|
|
obj-$(CONFIG_MRST) += intel_mrst.o
|
|
obj-$(CONFIG_ISL29003) += isl29003.o
|
|
+obj-$(CONFIG_MRST_VIB) += mrst_vib.o
|
|
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
|
|
obj-$(CONFIG_DS1682) += ds1682.o
|
|
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
|
|
--- /dev/null
|
|
+++ b/drivers/misc/mrst_vib.c
|
|
@@ -0,0 +1,99 @@
|
|
+/*
|
|
+ * mrst_vib.c - Intel vibrator Driver
|
|
+ *
|
|
+ * Copyright (C) 2008 Intel Corp
|
|
+ *
|
|
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+ *
|
|
+ * 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; version 2 of the License.
|
|
+ *
|
|
+ * 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/kernel.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <asm/ipc_defs.h>
|
|
+
|
|
+
|
|
+MODULE_AUTHOR("Kalhan Trisal");
|
|
+MODULE_DESCRIPTION("Intel Moorestown Thermal Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|
|
+#define VIB_START 1
|
|
+#define VIB_STOP 2
|
|
+static struct platform_device *vib_pdev;
|
|
+
|
|
+static ssize_t vib_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+
|
|
+ struct ipc_pmic_reg_data vib_power_reg_write = {0};
|
|
+ unsigned long val;
|
|
+
|
|
+ if (strict_strtoul(buf, 10, &val))
|
|
+ return -EINVAL;
|
|
+ if (val == VIB_START) {
|
|
+ vib_power_reg_write.ioc = TRUE;
|
|
+ vib_power_reg_write.pmic_reg_data[0].register_address = 0x49;
|
|
+ vib_power_reg_write.pmic_reg_data[0].value = 0xAD;
|
|
+ vib_power_reg_write.num_entries = 1;
|
|
+ if (ipc_pmic_register_write(&vib_power_reg_write, TRUE)) {
|
|
+ printk(KERN_WARNING "mrst_vib: failed to turn ON \
|
|
+ vib \n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else if (val == VIB_STOP) {
|
|
+ vib_power_reg_write.ioc = TRUE;
|
|
+ vib_power_reg_write.pmic_reg_data[0].register_address = 0x49;
|
|
+ vib_power_reg_write.pmic_reg_data[0].value = 0x14;
|
|
+ vib_power_reg_write.num_entries = 1;
|
|
+ if (ipc_pmic_register_write(&vib_power_reg_write, TRUE)) {
|
|
+ printk(KERN_WARNING "mrst_vib: failed to turn OFF \
|
|
+ Vibrator \n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else
|
|
+ return -EINVAL;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static struct device_attribute dev_attr_vib =
|
|
+ __ATTR(vib, S_IWUSR, NULL, vib_store);
|
|
+
|
|
+static int __init mrst_vib_init(void)
|
|
+{
|
|
+ int res = 0;
|
|
+
|
|
+ vib_pdev = platform_device_register_simple("mrst_vib", -1, NULL, 0);
|
|
+ if (IS_ERR(vib_pdev)) {
|
|
+ res = PTR_ERR(vib_pdev);
|
|
+ vib_pdev = NULL;
|
|
+ printk(KERN_WARNING "mrst_vib: unable to register platform \
|
|
+ device\n");
|
|
+ return res;
|
|
+ }
|
|
+ res = device_create_file(&vib_pdev->dev, &dev_attr_vib);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static void __exit mrst_vib_exit(void)
|
|
+{
|
|
+ device_remove_file(&vib_pdev->dev, &dev_attr_vib);
|
|
+ platform_device_unregister(vib_pdev);
|
|
+}
|
|
+
|
|
+module_init(mrst_vib_init);
|
|
+module_exit(mrst_vib_exit);
|