From cffaf6b15ff40cfbeafd0d4728ba3a5a5fb6155f Mon Sep 17 00:00:00 2001 From: Alan Olsen 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 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 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 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 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 Signed-off-by: Alan Olsen --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Kalhan Trisal > 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Kalhan Trisal 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Kalhan Trisal 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Kalhan Trisal 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 +#include +#include +#include + + +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);