Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging

* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging: (24 commits)
  hwmon: lis3: Release resources in case of failure
  hwmon: lis3: Short explanations of platform data fields
  hwmon: lis3: Enhance lis3 selftest with IRQ line test
  hwmon: lis3: use block read to access data registers
  hwmon: lis3: Adjust fuzziness for 8 bit device
  hwmon: lis3: New parameters to platform data
  hwmon: lis3: restore axis enabled bits
  hwmon: lis3: Power on corrections
  hwmon: lis3: Update coordinates at polled device open
  hwmon: lis3: Cleanup interrupt handling
  hwmon: lis3: regulator control
  hwmon: lis3: pm_runtime support
  Kirkwood: add fan support for Network Space Max v2
  hwmon: add generic GPIO fan driver
  hwmon: (coretemp) fix reading of microcode revision (v2)
  hwmon: ({core, pkg, via-cpu}temp) remove unnecessary CONFIG_HOTPLUG_CPU ifdefs
  hwmon: (pkgtemp) align driver initialization style with coretemp
  hwmon: LTC4261 Hardware monitoring driver
  hwmon: (lis3) add axes module parameter for custom axis-mapping
  hwmon: (hp_accel) Add HP Mini 510x family support
  ...
This commit is contained in:
Linus Torvalds 2010-10-25 16:25:31 -07:00
commit b20f9e5bdd
17 changed files with 1620 additions and 132 deletions

View file

@ -0,0 +1,63 @@
Kernel driver ltc4261
=====================
Supported chips:
* Linear Technology LTC4261
Prefix: 'ltc4261'
Addresses scanned: -
Datasheet:
http://cds.linear.com/docs/Datasheet/42612fb.pdf
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
The LTC4261/LTC4261-2 negative voltage Hot Swap controllers allow a board
to be safely inserted and removed from a live backplane.
Usage Notes
-----------
This driver does not probe for LTC4261 devices, since there is no register
which can be safely used to identify the chip. You will have to instantiate
the devices explicitly.
Example: the following will load the driver for an LTC4261 at address 0x10
on I2C bus #1:
$ modprobe ltc4261
$ echo ltc4261 0x10 > /sys/bus/i2c/devices/i2c-1/new_device
Sysfs entries
-------------
Voltage readings provided by this driver are reported as obtained from the ADC
registers. If a set of voltage divider resistors is installed, calculate the
real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the
value of the divider resistor against the measured voltage and R2 is the value
of the divider resistor against Ground.
Current reading provided by this driver is reported as obtained from the ADC
Current Sense register. The reported value assumes that a 1 mOhm sense resistor
is installed. If a different sense resistor is installed, calculate the real
current by dividing the reported value by the sense resistor value in mOhm.
The chip has two voltage sensors, but only one set of voltage alarm status bits.
In many many designs, those alarms are associated with the ADIN2 sensor, due to
the proximity of the ADIN2 pin to the OV pin. ADIN2 is, however, not available
on all chip variants. To ensure that the alarm condition is reported to the user,
report it with both voltage sensors.
in1_input ADIN2 voltage (mV)
in1_min_alarm ADIN/ADIN2 Undervoltage alarm
in1_max_alarm ADIN/ADIN2 Overvoltage alarm
in2_input ADIN voltage (mV)
in2_min_alarm ADIN/ADIN2 Undervoltage alarm
in2_max_alarm ADIN/ADIN2 Overvoltage alarm
curr1_input SENSE current (mA)
curr1_alarm SENSE overcurrent alarm

View file

@ -3765,6 +3765,13 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/scsi/sym53c8xx_2/
LTC4261 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: lm-sensors@lm-sensors.org
S: Maintained
F: Documentation/hwmon/ltc4261
F: drivers/hwmon/ltc4261.c
LTP (Linux Test Project)
M: Rishikesh K Rajak <risrajak@linux.vnet.ibm.com>
M: Garrett Cooper <yanegomi@gmail.com>

View file

@ -30,6 +30,7 @@
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/leds.h>
#include <linux/gpio-fan.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/kirkwood.h>
@ -136,6 +137,46 @@ static struct platform_device netspace_v2_leds = {
},
};
/*****************************************************************************
* GPIO fan
****************************************************************************/
/* Designed for fan 40x40x16: ADDA AD0412LB-D50 6000rpm@12v */
static struct gpio_fan_speed netspace_max_v2_fan_speed[] = {
{ 0, 0 },
{ 1500, 15 },
{ 1700, 14 },
{ 1800, 13 },
{ 2100, 12 },
{ 3100, 11 },
{ 3300, 10 },
{ 4300, 9 },
{ 5500, 8 },
};
static unsigned netspace_max_v2_fan_ctrl[] = { 22, 7, 33, 23 };
static struct gpio_fan_alarm netspace_max_v2_fan_alarm = {
.gpio = 25,
.active_low = 1,
};
static struct gpio_fan_platform_data netspace_max_v2_fan_data = {
.num_ctrl = ARRAY_SIZE(netspace_max_v2_fan_ctrl),
.ctrl = netspace_max_v2_fan_ctrl,
.alarm = &netspace_max_v2_fan_alarm,
.num_speed = ARRAY_SIZE(netspace_max_v2_fan_speed),
.speed = netspace_max_v2_fan_speed,
};
static struct platform_device netspace_max_v2_gpio_fan = {
.name = "gpio-fan",
.id = -1,
.dev = {
.platform_data = &netspace_max_v2_fan_data,
},
};
/*****************************************************************************
* General Setup
****************************************************************************/
@ -205,6 +246,8 @@ static void __init netspace_v2_init(void)
platform_device_register(&netspace_v2_leds);
platform_device_register(&netspace_v2_gpio_leds);
platform_device_register(&netspace_v2_gpio_buttons);
if (machine_is_netspace_max_v2())
platform_device_register(&netspace_max_v2_gpio_fan);
if (gpio_request(NETSPACE_V2_GPIO_POWER_OFF, "power-off") == 0 &&
gpio_direction_output(NETSPACE_V2_GPIO_POWER_OFF, 0) == 0)

View file

@ -399,6 +399,15 @@ config SENSORS_GL520SM
This driver can also be built as a module. If so, the module
will be called gl520sm.
config SENSORS_GPIO_FAN
tristate "GPIO fan"
depends on GENERIC_GPIO
help
If you say yes here you get support for fans connected to GPIO lines.
This driver can also be built as a module. If so, the module
will be called gpio-fan.
config SENSORS_CORETEMP
tristate "Intel Core/Core2/Atom temperature sensor"
depends on X86 && PCI && EXPERIMENTAL
@ -654,6 +663,17 @@ config SENSORS_LTC4245
This driver can also be built as a module. If so, the module will
be called ltc4245.
config SENSORS_LTC4261
tristate "Linear Technology LTC4261"
depends on I2C && EXPERIMENTAL
default n
help
If you say yes here you get support for Linear Technology LTC4261
Negative Voltage Hot Swap Controller I2C interface.
This driver can also be built as a module. If so, the module will
be called ltc4261.
config SENSORS_LM95241
tristate "National Semiconductor LM95241 sensor chip"
depends on I2C

View file

@ -51,6 +51,7 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
@ -79,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o

View file

@ -21,7 +21,6 @@
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
@ -280,11 +279,9 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
case 0x1a:
dev_warn(dev, "TjMax is assumed as 100 C!\n");
return 100000;
break;
case 0x17:
case 0x1c: /* Atom CPUs */
return adjust_tjmax(c, id, dev);
break;
default:
dev_warn(dev, "CPU (model=0x%x) is not supported yet,"
" using default TjMax of 100C.\n", c->x86_model);
@ -292,6 +289,15 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
}
}
static void __devinit get_ucode_rev_on_cpu(void *edx)
{
u32 eax;
wrmsr(MSR_IA32_UCODE_REV, 0, 0);
sync_core();
rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
}
static int __devinit coretemp_probe(struct platform_device *pdev)
{
struct coretemp_data *data;
@ -327,8 +333,15 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) {
/* check for microcode update */
rdmsr_on_cpu(data->id, MSR_IA32_UCODE_REV, &eax, &edx);
if (edx < 0x39) {
err = smp_call_function_single(data->id, get_ucode_rev_on_cpu,
&edx, 1);
if (err) {
dev_err(&pdev->dev,
"Cannot determine microcode revision of "
"CPU#%u (%d)!\n", data->id, err);
err = -ENODEV;
goto exit_free;
} else if (edx < 0x39) {
err = -ENODEV;
dev_err(&pdev->dev,
"Errata AE18 not fixed, update BIOS or "
@ -490,7 +503,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
return err;
}
static void coretemp_device_remove(unsigned int cpu)
static void __cpuinit coretemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p;
unsigned int i;
@ -569,9 +582,8 @@ static int __init coretemp_init(void)
static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
#ifdef CONFIG_HOTPLUG_CPU
unregister_hotcpu_notifier(&coretemp_cpu_notifier);
#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);

558
drivers/hwmon/gpio-fan.c Normal file
View file

@ -0,0 +1,558 @@
/*
* gpio-fan.c - Hwmon driver for fans connected to GPIO lines.
*
* Copyright (C) 2010 LaCie
*
* Author: Simon Guinot <sguinot@lacie.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>
#include <linux/gpio.h>
#include <linux/gpio-fan.h>
struct gpio_fan_data {
struct platform_device *pdev;
struct device *hwmon_dev;
struct mutex lock; /* lock GPIOs operations. */
int num_ctrl;
unsigned *ctrl;
int num_speed;
struct gpio_fan_speed *speed;
int speed_index;
#ifdef CONFIG_PM
int resume_speed;
#endif
bool pwm_enable;
struct gpio_fan_alarm *alarm;
struct work_struct alarm_work;
};
/*
* Alarm GPIO.
*/
static void fan_alarm_notify(struct work_struct *ws)
{
struct gpio_fan_data *fan_data =
container_of(ws, struct gpio_fan_data, alarm_work);
sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm");
kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE);
}
static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
{
struct gpio_fan_data *fan_data = dev_id;
schedule_work(&fan_data->alarm_work);
return IRQ_NONE;
}
static ssize_t show_fan_alarm(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
struct gpio_fan_alarm *alarm = fan_data->alarm;
int value = gpio_get_value(alarm->gpio);
if (alarm->active_low)
value = !value;
return sprintf(buf, "%d\n", value);
}
static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL);
static int fan_alarm_init(struct gpio_fan_data *fan_data,
struct gpio_fan_alarm *alarm)
{
int err;
int alarm_irq;
struct platform_device *pdev = fan_data->pdev;
fan_data->alarm = alarm;
err = gpio_request(alarm->gpio, "GPIO fan alarm");
if (err)
return err;
err = gpio_direction_input(alarm->gpio);
if (err)
goto err_free_gpio;
err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm);
if (err)
goto err_free_gpio;
/*
* If the alarm GPIO don't support interrupts, just leave
* without initializing the fail notification support.
*/
alarm_irq = gpio_to_irq(alarm->gpio);
if (alarm_irq < 0)
return 0;
INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED,
"GPIO fan alarm", fan_data);
if (err)
goto err_free_sysfs;
return 0;
err_free_sysfs:
device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
err_free_gpio:
gpio_free(alarm->gpio);
return err;
}
static void fan_alarm_free(struct gpio_fan_data *fan_data)
{
struct platform_device *pdev = fan_data->pdev;
int alarm_irq = gpio_to_irq(fan_data->alarm->gpio);
if (alarm_irq >= 0)
free_irq(alarm_irq, fan_data);
device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
gpio_free(fan_data->alarm->gpio);
}
/*
* Control GPIOs.
*/
/* Must be called with fan_data->lock held, except during initialization. */
static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
{
int i;
for (i = 0; i < fan_data->num_ctrl; i++)
gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1);
}
static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
{
int i;
int ctrl_val = 0;
for (i = 0; i < fan_data->num_ctrl; i++) {
int value;
value = gpio_get_value(fan_data->ctrl[i]);
ctrl_val |= (value << i);
}
return ctrl_val;
}
/* Must be called with fan_data->lock held, except during initialization. */
static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
{
if (fan_data->speed_index == speed_index)
return;
__set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
fan_data->speed_index = speed_index;
}
static int get_fan_speed_index(struct gpio_fan_data *fan_data)
{
int ctrl_val = __get_fan_ctrl(fan_data);
int i;
for (i = 0; i < fan_data->num_speed; i++)
if (fan_data->speed[i].ctrl_val == ctrl_val)
return i;
dev_warn(&fan_data->pdev->dev,
"missing speed array entry for GPIO value 0x%x\n", ctrl_val);
return -EINVAL;
}
static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm)
{
struct gpio_fan_speed *speed = fan_data->speed;
int i;
for (i = 0; i < fan_data->num_speed; i++)
if (speed[i].rpm >= rpm)
return i;
return fan_data->num_speed - 1;
}
static ssize_t show_pwm(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
return sprintf(buf, "%d\n", pwm);
}
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long pwm;
int speed_index;
int ret = count;
if (strict_strtoul(buf, 10, &pwm) || pwm > 255)
return -EINVAL;
mutex_lock(&fan_data->lock);
if (!fan_data->pwm_enable) {
ret = -EPERM;
goto exit_unlock;
}
speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
set_fan_speed(fan_data, speed_index);
exit_unlock:
mutex_unlock(&fan_data->lock);
return ret;
}
static ssize_t show_pwm_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", fan_data->pwm_enable);
}
static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long val;
if (strict_strtoul(buf, 10, &val) || val > 1)
return -EINVAL;
if (fan_data->pwm_enable == val)
return count;
mutex_lock(&fan_data->lock);
fan_data->pwm_enable = val;
/* Disable manual control mode: set fan at full speed. */
if (val == 0)
set_fan_speed(fan_data, fan_data->num_speed - 1);
mutex_unlock(&fan_data->lock);
return count;
}
static ssize_t show_pwm_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "0\n");
}
static ssize_t show_rpm_min(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", fan_data->speed[0].rpm);
}
static ssize_t show_rpm_max(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n",
fan_data->speed[fan_data->num_speed - 1].rpm);
}
static ssize_t show_rpm(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm);
}
static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long rpm;
int ret = count;
if (strict_strtoul(buf, 10, &rpm))
return -EINVAL;
mutex_lock(&fan_data->lock);
if (!fan_data->pwm_enable) {
ret = -EPERM;
goto exit_unlock;
}
set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
exit_unlock:
mutex_unlock(&fan_data->lock);
return ret;
}
static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
show_pwm_enable, set_pwm_enable);
static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL);
static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL);
static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL);
static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm);
static struct attribute *gpio_fan_ctrl_attributes[] = {
&dev_attr_pwm1.attr,
&dev_attr_pwm1_enable.attr,
&dev_attr_pwm1_mode.attr,
&dev_attr_fan1_input.attr,
&dev_attr_fan1_target.attr,
&dev_attr_fan1_min.attr,
&dev_attr_fan1_max.attr,
NULL
};
static const struct attribute_group gpio_fan_ctrl_group = {
.attrs = gpio_fan_ctrl_attributes,
};
static int fan_ctrl_init(struct gpio_fan_data *fan_data,
struct gpio_fan_platform_data *pdata)
{
struct platform_device *pdev = fan_data->pdev;
int num_ctrl = pdata->num_ctrl;
unsigned *ctrl = pdata->ctrl;
int i, err;
for (i = 0; i < num_ctrl; i++) {
err = gpio_request(ctrl[i], "GPIO fan control");
if (err)
goto err_free_gpio;
err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i]));
if (err) {
gpio_free(ctrl[i]);
goto err_free_gpio;
}
}
err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
if (err)
goto err_free_gpio;
fan_data->num_ctrl = num_ctrl;
fan_data->ctrl = ctrl;
fan_data->num_speed = pdata->num_speed;
fan_data->speed = pdata->speed;
fan_data->pwm_enable = true; /* Enable manual fan speed control. */
fan_data->speed_index = get_fan_speed_index(fan_data);
if (fan_data->speed_index < 0) {
err = -ENODEV;
goto err_free_gpio;
}
return 0;
err_free_gpio:
for (i = i - 1; i >= 0; i--)
gpio_free(ctrl[i]);
return err;
}
static void fan_ctrl_free(struct gpio_fan_data *fan_data)
{
struct platform_device *pdev = fan_data->pdev;
int i;
sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
for (i = 0; i < fan_data->num_ctrl; i++)
gpio_free(fan_data->ctrl[i]);
}
/*
* Platform driver.
*/
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "gpio-fan\n");
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static int __devinit gpio_fan_probe(struct platform_device *pdev)
{
int err;
struct gpio_fan_data *fan_data;
struct gpio_fan_platform_data *pdata = pdev->dev.platform_data;
if (!pdata)
return -EINVAL;
fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL);
if (!fan_data)
return -ENOMEM;
fan_data->pdev = pdev;
platform_set_drvdata(pdev, fan_data);
mutex_init(&fan_data->lock);
/* Configure alarm GPIO if available. */
if (pdata->alarm) {
err = fan_alarm_init(fan_data, pdata->alarm);
if (err)
goto err_free_data;
}
/* Configure control GPIOs if available. */
if (pdata->ctrl && pdata->num_ctrl > 0) {
if (!pdata->speed || pdata->num_speed <= 1) {
err = -EINVAL;
goto err_free_alarm;
}
err = fan_ctrl_init(fan_data, pdata);
if (err)
goto err_free_alarm;
}
err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
goto err_free_ctrl;
/* Make this driver part of hwmon class. */
fan_data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(fan_data->hwmon_dev)) {
err = PTR_ERR(fan_data->hwmon_dev);
goto err_remove_name;
}
dev_info(&pdev->dev, "GPIO fan initialized\n");
return 0;
err_remove_name:
device_remove_file(&pdev->dev, &dev_attr_name);
err_free_ctrl:
if (fan_data->ctrl)
fan_ctrl_free(fan_data);
err_free_alarm:
if (fan_data->alarm)
fan_alarm_free(fan_data);
err_free_data:
platform_set_drvdata(pdev, NULL);
kfree(fan_data);
return err;
}
static int __devexit gpio_fan_remove(struct platform_device *pdev)
{
struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
hwmon_device_unregister(fan_data->hwmon_dev);
device_remove_file(&pdev->dev, &dev_attr_name);
if (fan_data->alarm)
fan_alarm_free(fan_data);
if (fan_data->ctrl)
fan_ctrl_free(fan_data);
kfree(fan_data);
return 0;
}
#ifdef CONFIG_PM
static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state)
{
struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
if (fan_data->ctrl) {
fan_data->resume_speed = fan_data->speed_index;
set_fan_speed(fan_data, 0);
}
return 0;
}
static int gpio_fan_resume(struct platform_device *pdev)
{
struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
if (fan_data->ctrl)
set_fan_speed(fan_data, fan_data->resume_speed);
return 0;
}
#else
#define gpio_fan_suspend NULL
#define gpio_fan_resume NULL
#endif
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
.remove = __devexit_p(gpio_fan_remove),
.suspend = gpio_fan_suspend,
.resume = gpio_fan_resume,
.driver = {
.name = "gpio-fan",
},
};
static int __init gpio_fan_init(void)
{
return platform_driver_register(&gpio_fan_driver);
}
static void __exit gpio_fan_exit(void)
{
platform_driver_unregister(&gpio_fan_driver);
}
module_init(gpio_fan_init);
module_exit(gpio_fan_exit);
MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
MODULE_DESCRIPTION("GPIO FAN driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio-fan");

View file

@ -146,7 +146,7 @@ int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)
static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi)
{
lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data);
lis3_dev.ac = *((union axis_conversion *)dmi->driver_data);
printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident);
return 1;
@ -154,16 +154,19 @@ static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi)
/* Represents, for each axis seen by userspace, the corresponding hw axis (+1).
* If the value is negative, the opposite of the hw value is used. */
static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3};
static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3};
static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3};
static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3};
static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3};
static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3};
static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3};
static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3};
static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3};
static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3};
#define DEFINE_CONV(name, x, y, z) \
static union axis_conversion lis3lv02d_axis_##name = \
{ .as_array = { x, y, z } }
DEFINE_CONV(normal, 1, 2, 3);
DEFINE_CONV(y_inverted, 1, -2, 3);
DEFINE_CONV(x_inverted, -1, 2, 3);
DEFINE_CONV(z_inverted, 1, 2, -3);
DEFINE_CONV(xy_swap, 2, 1, 3);
DEFINE_CONV(xy_rotated_left, -2, 1, 3);
DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3);
DEFINE_CONV(xy_swap_inverted, -2, -1, 3);
DEFINE_CONV(xy_rotated_right, 2, -1, 3);
DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3);
#define AXIS_DMI_MATCH(_ident, _name, _axis) { \
.ident = _ident, \
@ -222,7 +225,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd),
AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd),
{ NULL, }
/* Laptop models without axis info (yet):
* "NC6910" "HP Compaq 6910"
@ -299,7 +302,10 @@ static int lis3lv02d_add(struct acpi_device *device)
lis3lv02d_enum_resources(device);
/* If possible use a "standard" axes order */
if (dmi_check_system(lis3lv02d_dmi_ids) == 0) {
if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) {
printk(KERN_INFO DRIVER_NAME ": Using custom axes %d,%d,%d\n",
lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z);
} else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) {
printk(KERN_INFO DRIVER_NAME ": laptop model unknown, "
"using default axes configuration\n");
lis3_dev.ac = lis3lv02d_axis_normal;

View file

@ -31,9 +31,11 @@
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/freezer.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
#include <asm/atomic.h>
#include "lis3lv02d.h"
@ -43,6 +45,16 @@
#define MDPS_POLL_INTERVAL 50
#define MDPS_POLL_MIN 0
#define MDPS_POLL_MAX 2000
#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */
#define SELFTEST_OK 0
#define SELFTEST_FAIL -1
#define SELFTEST_IRQ -2
#define IRQ_LINE0 0
#define IRQ_LINE1 1
/*
* The sensor can also generate interrupts (DRDY) but it's pretty pointless
* because they are generated even if the data do not change. So it's better
@ -66,8 +78,10 @@
#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
#define LIS3_DEFAULT_FUZZ 3
#define LIS3_DEFAULT_FLAT 3
#define LIS3_DEFAULT_FUZZ_12B 3
#define LIS3_DEFAULT_FLAT_12B 3
#define LIS3_DEFAULT_FUZZ_8B 1
#define LIS3_DEFAULT_FLAT_8B 1
struct lis3lv02d lis3_dev = {
.misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),
@ -75,6 +89,30 @@ struct lis3lv02d lis3_dev = {
EXPORT_SYMBOL_GPL(lis3_dev);
/* just like param_set_int() but does sanity-check so that it won't point
* over the axis array size
*/
static int param_set_axis(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
if (!ret) {
int val = *(int *)kp->arg;
if (val < 0)
val = -val;
if (!val || val > 3)
return -EINVAL;
}
return ret;
}
static struct kernel_param_ops param_ops_axis = {
.set = param_set_axis,
.get = param_get_int,
};
module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644);
MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions");
static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg)
{
s8 lo;
@ -123,9 +161,24 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
int position[3];
int i;
position[0] = lis3->read_data(lis3, OUTX);
position[1] = lis3->read_data(lis3, OUTY);
position[2] = lis3->read_data(lis3, OUTZ);
if (lis3->blkread) {
if (lis3_dev.whoami == WAI_12B) {
u16 data[3];
lis3->blkread(lis3, OUTX_L, 6, (u8 *)data);
for (i = 0; i < 3; i++)
position[i] = (s16)le16_to_cpu(data[i]);
} else {
u8 data[5];
/* Data: x, dummy, y, dummy, z */
lis3->blkread(lis3, OUTX, 5, data);
for (i = 0; i < 3; i++)
position[i] = (s8)data[i * 2];
}
} else {
position[0] = lis3->read_data(lis3, OUTX);
position[1] = lis3->read_data(lis3, OUTY);
position[2] = lis3->read_data(lis3, OUTZ);
}
for (i = 0; i < 3; i++)
position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;
@ -138,6 +191,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
/* conversion btw sampling rate and the register values */
static int lis3_12_rates[4] = {40, 160, 640, 2560};
static int lis3_8_rates[2] = {100, 400};
static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
/* ODR is Output Data Rate */
static int lis3lv02d_get_odr(void)
@ -156,6 +210,9 @@ static int lis3lv02d_set_odr(int rate)
u8 ctrl;
int i, len, shift;
if (!rate)
return -EINVAL;
lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
ctrl &= ~lis3_dev.odr_mask;
len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
@ -172,19 +229,42 @@ static int lis3lv02d_set_odr(int rate)
static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
{
u8 reg;
u8 ctlreg, reg;
s16 x, y, z;
u8 selftest;
int ret;
u8 ctrl_reg_data;
unsigned char irq_cfg;
mutex_lock(&lis3->mutex);
if (lis3_dev.whoami == WAI_12B)
selftest = CTRL1_ST;
else
selftest = CTRL1_STP;
lis3->read(lis3, CTRL_REG1, &reg);
lis3->write(lis3, CTRL_REG1, (reg | selftest));
irq_cfg = lis3->irq_cfg;
if (lis3_dev.whoami == WAI_8B) {
lis3->data_ready_count[IRQ_LINE0] = 0;
lis3->data_ready_count[IRQ_LINE1] = 0;
/* Change interrupt cfg to data ready for selftest */
atomic_inc(&lis3_dev.wake_thread);
lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY;
lis3->read(lis3, CTRL_REG3, &ctrl_reg_data);
lis3->write(lis3, CTRL_REG3, (ctrl_reg_data &
~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) |
(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
}
if (lis3_dev.whoami == WAI_3DC) {
ctlreg = CTRL_REG4;
selftest = CTRL4_ST0;
} else {
ctlreg = CTRL_REG1;
if (lis3_dev.whoami == WAI_12B)
selftest = CTRL1_ST;
else
selftest = CTRL1_STP;
}
lis3->read(lis3, ctlreg, &reg);
lis3->write(lis3, ctlreg, (reg | selftest));
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
/* Read directly to avoid axis remap */
@ -193,7 +273,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
z = lis3->read_data(lis3, OUTZ);
/* back to normal settings */
lis3->write(lis3, CTRL_REG1, reg);
lis3->write(lis3, ctlreg, reg);
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
results[0] = x - lis3->read_data(lis3, OUTX);
@ -201,13 +281,33 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
results[2] = z - lis3->read_data(lis3, OUTZ);
ret = 0;
if (lis3_dev.whoami == WAI_8B) {
/* Restore original interrupt configuration */
atomic_dec(&lis3_dev.wake_thread);
lis3->write(lis3, CTRL_REG3, ctrl_reg_data);
lis3->irq_cfg = irq_cfg;
if ((irq_cfg & LIS3_IRQ1_MASK) &&
lis3->data_ready_count[IRQ_LINE0] < 2) {
ret = SELFTEST_IRQ;
goto fail;
}
if ((irq_cfg & LIS3_IRQ2_MASK) &&
lis3->data_ready_count[IRQ_LINE1] < 2) {
ret = SELFTEST_IRQ;
goto fail;
}
}
if (lis3->pdata) {
int i;
for (i = 0; i < 3; i++) {
/* Check against selftest acceptance limits */
if ((results[i] < lis3->pdata->st_min_limits[i]) ||
(results[i] > lis3->pdata->st_max_limits[i])) {
ret = -EIO;
ret = SELFTEST_FAIL;
goto fail;
}
}
@ -219,10 +319,46 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
return ret;
}
/*
* Order of registers in the list affects to order of the restore process.
* Perhaps it is a good idea to set interrupt enable register as a last one
* after all other configurations
*/
static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1,
FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2,
CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ,
CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW,
CTRL_REG1, CTRL_REG2, CTRL_REG3};
static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H,
FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H,
DD_THSE_L, DD_THSE_H,
CTRL_REG1, CTRL_REG3, CTRL_REG2};
static inline void lis3_context_save(struct lis3lv02d *lis3)
{
int i;
for (i = 0; i < lis3->regs_size; i++)
lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]);
lis3->regs_stored = true;
}
static inline void lis3_context_restore(struct lis3lv02d *lis3)
{
int i;
if (lis3->regs_stored)
for (i = 0; i < lis3->regs_size; i++)
lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]);
}
void lis3lv02d_poweroff(struct lis3lv02d *lis3)
{
if (lis3->reg_ctrl)
lis3_context_save(lis3);
/* disable X,Y,Z axis and power down */
lis3->write(lis3, CTRL_REG1, 0x00);
if (lis3->reg_ctrl)
lis3->reg_ctrl(lis3, LIS3_REG_OFF);
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
@ -232,19 +368,24 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->init(lis3);
/* LIS3 power on delay is quite long */
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
/*
* Common configuration
* BDU: (12 bits sensors only) LSB and MSB values are not updated until
* both have been read. So the value read will always be correct.
* Set BOOT bit to refresh factory tuning values.
*/
if (lis3->whoami == WAI_12B) {
lis3->read(lis3, CTRL_REG2, &reg);
reg |= CTRL2_BDU;
lis3->write(lis3, CTRL_REG2, reg);
}
lis3->read(lis3, CTRL_REG2, &reg);
if (lis3->whoami == WAI_12B)
reg |= CTRL2_BDU | CTRL2_BOOT;
else
reg |= CTRL2_BOOT_8B;
lis3->write(lis3, CTRL_REG2, reg);
/* LIS3 power on delay is quite long */
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
if (lis3->reg_ctrl)
lis3_context_restore(lis3);
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
@ -262,6 +403,27 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
mutex_unlock(&lis3_dev.mutex);
}
static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
{
if (lis3_dev.pm_dev)
pm_runtime_get_sync(lis3_dev.pm_dev);
if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev)
atomic_set(&lis3_dev.wake_thread, 1);
/*
* Update coordinates for the case where poll interval is 0 and
* the chip in running purely under interrupt control
*/
lis3lv02d_joystick_poll(pidev);
}
static void lis3lv02d_joystick_close(struct input_polled_dev *pidev)
{
atomic_set(&lis3_dev.wake_thread, 0);
if (lis3_dev.pm_dev)
pm_runtime_put(lis3_dev.pm_dev);
}
static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
{
if (!test_bit(0, &lis3_dev.misc_opened))
@ -277,8 +439,7 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
wake_up_interruptible(&lis3_dev.misc_wait);
kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
out:
if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev &&
lis3_dev.idev->input->users)
if (atomic_read(&lis3_dev.wake_thread))
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
@ -309,44 +470,41 @@ static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3)
mutex_unlock(&lis3->mutex);
}
static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3)
static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index)
{
u8 wu1_src;
u8 wu2_src;
int dummy;
lis3->read(lis3, FF_WU_SRC_1, &wu1_src);
lis3->read(lis3, FF_WU_SRC_2, &wu2_src);
wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0;
wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0;
/* joystick poll is internally protected by the lis3->mutex. */
if (wu1_src || wu2_src)
lis3lv02d_joystick_poll(lis3_dev.idev);
/* Dummy read to ack interrupt */
lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy);
lis3->data_ready_count[index]++;
}
static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data)
{
struct lis3lv02d *lis3 = data;
u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK;
if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK)
if (irq_cfg == LIS3_IRQ1_CLICK)
lis302dl_interrupt_handle_click(lis3);
else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY))
lis302dl_data_ready(lis3, IRQ_LINE0);
else
lis302dl_interrupt_handle_ff_wu(lis3);
lis3lv02d_joystick_poll(lis3->idev);
return IRQ_HANDLED;
}
static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
{
struct lis3lv02d *lis3 = data;
u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK;
if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK)
if (irq_cfg == LIS3_IRQ2_CLICK)
lis302dl_interrupt_handle_click(lis3);
else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY))
lis302dl_data_ready(lis3, IRQ_LINE1);
else
lis302dl_interrupt_handle_ff_wu(lis3);
lis3lv02d_joystick_poll(lis3->idev);
return IRQ_HANDLED;
}
@ -356,6 +514,9 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
if (test_and_set_bit(0, &lis3_dev.misc_opened))
return -EBUSY; /* already open */
if (lis3_dev.pm_dev)
pm_runtime_get_sync(lis3_dev.pm_dev);
atomic_set(&lis3_dev.count, 0);
return 0;
}
@ -364,6 +525,8 @@ static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
{
fasync_helper(-1, file, 0, &lis3_dev.async_queue);
clear_bit(0, &lis3_dev.misc_opened); /* release the device */
if (lis3_dev.pm_dev)
pm_runtime_put(lis3_dev.pm_dev);
return 0;
}
@ -460,6 +623,8 @@ int lis3lv02d_joystick_enable(void)
return -ENOMEM;
lis3_dev.idev->poll = lis3lv02d_joystick_poll;
lis3_dev.idev->open = lis3lv02d_joystick_open;
lis3_dev.idev->close = lis3lv02d_joystick_close;
lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;
lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;
@ -473,8 +638,16 @@ int lis3lv02d_joystick_enable(void)
set_bit(EV_ABS, input_dev->evbit);
max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY;
flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY;
if (lis3_dev.whoami == WAI_12B) {
fuzz = LIS3_DEFAULT_FUZZ_12B;
flat = LIS3_DEFAULT_FLAT_12B;
} else {
fuzz = LIS3_DEFAULT_FUZZ_8B;
flat = LIS3_DEFAULT_FLAT_8B;
}
fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY;
flat = (flat * lis3_dev.scale) / LIS3_ACCURACY;
input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
@ -512,14 +685,47 @@ void lis3lv02d_joystick_disable(void)
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
/* Sysfs stuff */
static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3)
{
/*
* SYSFS functions are fast visitors so put-call
* immediately after the get-call. However, keep
* chip running for a while and schedule delayed
* suspend. This way periodic sysfs calls doesn't
* suffer from relatively long power up time.
*/
if (lis3->pm_dev) {
pm_runtime_get_sync(lis3->pm_dev);
pm_runtime_put_noidle(lis3->pm_dev);
pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY);
}
}
static ssize_t lis3lv02d_selftest_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int result;
s16 values[3];
result = lis3lv02d_selftest(&lis3_dev, values);
return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL",
static const char ok[] = "OK";
static const char fail[] = "FAIL";
static const char irq[] = "FAIL_IRQ";
const char *res;
lis3lv02d_sysfs_poweron(&lis3_dev);
switch (lis3lv02d_selftest(&lis3_dev, values)) {
case SELFTEST_FAIL:
res = fail;
break;
case SELFTEST_IRQ:
res = irq;
break;
case SELFTEST_OK:
default:
res = ok;
break;
}
return sprintf(buf, "%s %d %d %d\n", res,
values[0], values[1], values[2]);
}
@ -528,6 +734,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
{
int x, y, z;
lis3lv02d_sysfs_poweron(&lis3_dev);
mutex_lock(&lis3_dev.mutex);
lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
mutex_unlock(&lis3_dev.mutex);
@ -537,6 +744,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
static ssize_t lis3lv02d_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
lis3lv02d_sysfs_poweron(&lis3_dev);
return sprintf(buf, "%d\n", lis3lv02d_get_odr());
}
@ -549,6 +757,7 @@ static ssize_t lis3lv02d_rate_set(struct device *dev,
if (strict_strtoul(buf, 0, &rate))
return -EINVAL;
lis3lv02d_sysfs_poweron(&lis3_dev);
if (lis3lv02d_set_odr(rate))
return -EINVAL;
@ -585,6 +794,18 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
{
sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
platform_device_unregister(lis3->pdev);
if (lis3->pm_dev) {
/* Barrier after the sysfs remove */
pm_runtime_barrier(lis3->pm_dev);
/* SYSFS may have left chip running. Turn off if necessary */
if (!pm_runtime_suspended(lis3->pm_dev))
lis3lv02d_poweroff(&lis3_dev);
pm_runtime_disable(lis3->pm_dev);
pm_runtime_set_suspended(lis3->pm_dev);
}
kfree(lis3->reg_cache);
return 0;
}
EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
@ -616,16 +837,16 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
if (p->wakeup_flags) {
dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
/* default to 2.5ms for now */
dev->write(dev, FF_WU_DURATION_1, 1);
/* pdata value + 1 to keep this backward compatible*/
dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1);
ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/
}
if (p->wakeup_flags2) {
dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);
dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
/* default to 2.5ms for now */
dev->write(dev, FF_WU_DURATION_2, 1);
/* pdata value + 1 to keep this backward compatible*/
dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1);
ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/
}
/* Configure hipass filters */
@ -635,8 +856,8 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
err = request_threaded_irq(p->irq2,
NULL,
lis302dl_interrupt_thread2_8b,
IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
(p->irq_flags2 & IRQF_TRIGGER_MASK),
DRIVER_NAME, &lis3_dev);
if (err < 0)
printk(KERN_ERR DRIVER_NAME
@ -652,6 +873,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
{
int err;
irq_handler_t thread_fn;
int irq_flags = 0;
dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
@ -664,6 +886,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
dev->odrs = lis3_12_rates;
dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
dev->scale = LIS3_SENSITIVITY_12B;
dev->regs = lis3_wai12_regs;
dev->regs_size = ARRAY_SIZE(lis3_wai12_regs);
break;
case WAI_8B:
printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n");
@ -673,6 +897,17 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
dev->odrs = lis3_8_rates;
dev->odr_mask = CTRL1_DR;
dev->scale = LIS3_SENSITIVITY_8B;
dev->regs = lis3_wai8_regs;
dev->regs_size = ARRAY_SIZE(lis3_wai8_regs);
break;
case WAI_3DC:
printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n");
dev->read_data = lis3lv02d_read_8;
dev->mdps_max_val = 128;
dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
dev->odrs = lis3_3dc_rates;
dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
dev->scale = LIS3_SENSITIVITY_8B;
break;
default:
printk(KERN_ERR DRIVER_NAME
@ -680,11 +915,25 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
return -EINVAL;
}
dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
sizeof(lis3_wai12_regs)), GFP_KERNEL);
if (dev->reg_cache == NULL) {
printk(KERN_ERR DRIVER_NAME "out of memory\n");
return -ENOMEM;
}
mutex_init(&dev->mutex);
atomic_set(&dev->wake_thread, 0);
lis3lv02d_add_fs(dev);
lis3lv02d_poweron(dev);
if (dev->pm_dev) {
pm_runtime_set_active(dev->pm_dev);
pm_runtime_enable(dev->pm_dev);
}
if (lis3lv02d_joystick_enable())
printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n");
@ -696,8 +945,14 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
if (dev->whoami == WAI_8B)
lis3lv02d_8b_configure(dev, p);
irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK;
dev->irq_cfg = p->irq_cfg;
if (p->irq_cfg)
dev->write(dev, CTRL_REG3, p->irq_cfg);
if (p->default_rate)
lis3lv02d_set_odr(p->default_rate);
}
/* bail if we did not get an IRQ from the bus layer */
@ -725,7 +980,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
err = request_threaded_irq(dev->irq, lis302dl_interrupt,
thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
irq_flags,
DRIVER_NAME, &lis3_dev);
if (err < 0) {

View file

@ -20,6 +20,7 @@
*/
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/regulator/consumer.h>
/*
* This driver tries to support the "digital" accelerometer chips from
@ -45,6 +46,7 @@ enum lis3_reg {
CTRL_REG1 = 0x20,
CTRL_REG2 = 0x21,
CTRL_REG3 = 0x22,
CTRL_REG4 = 0x23,
HP_FILTER_RESET = 0x23,
STATUS_REG = 0x27,
OUTX_L = 0x28,
@ -93,6 +95,7 @@ enum lis3lv02d_reg {
};
enum lis3_who_am_i {
WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */
WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
@ -118,6 +121,13 @@ enum lis3lv02d_ctrl1_8b {
CTRL1_DR = 0x80,
};
enum lis3lv02d_ctrl1_3dc {
CTRL1_ODR0 = 0x10,
CTRL1_ODR1 = 0x20,
CTRL1_ODR2 = 0x40,
CTRL1_ODR3 = 0x80,
};
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
@ -129,9 +139,18 @@ enum lis3lv02d_ctrl2 {
CTRL2_FS = 0x80, /* Full Scale selection */
};
enum lis3lv02d_ctrl4_3dc {
CTRL4_SIM = 0x01,
CTRL4_ST0 = 0x02,
CTRL4_ST1 = 0x04,
CTRL4_FS0 = 0x10,
CTRL4_FS1 = 0x20,
};
enum lis302d_ctrl2 {
HP_FF_WU2 = 0x08,
HP_FF_WU1 = 0x04,
CTRL2_BOOT_8B = 0x40,
};
enum lis3lv02d_ctrl3 {
@ -206,19 +225,33 @@ enum lis3lv02d_click_src_8b {
CLICK_IA = 0x40,
};
struct axis_conversion {
s8 x;
s8 y;
s8 z;
enum lis3lv02d_reg_state {
LIS3_REG_OFF = 0x00,
LIS3_REG_ON = 0x01,
};
union axis_conversion {
struct {
int x, y, z;
};
int as_array[3];
};
struct lis3lv02d {
void *bus_priv; /* used by the bus layer only */
struct device *pm_dev; /* for pm_runtime purposes */
int (*init) (struct lis3lv02d *lis3);
int (*write) (struct lis3lv02d *lis3, int reg, u8 val);
int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret);
int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret);
int (*reg_ctrl) (struct lis3lv02d *lis3, bool state);
int *odrs; /* Supported output data rates */
u8 *regs; /* Regs to store / restore */
int regs_size;
u8 *reg_cache;
bool regs_stored;
u8 odr_mask; /* ODR bit mask */
u8 whoami; /* indicates measurement precision */
s16 (*read_data) (struct lis3lv02d *lis3, int reg);
@ -231,14 +264,18 @@ struct lis3lv02d {
struct input_polled_dev *idev; /* input device */
struct platform_device *pdev; /* platform device */
struct regulator_bulk_data regulators[2];
atomic_t count; /* interrupt count after last read */
struct axis_conversion ac; /* hw -> logical axis */
union axis_conversion ac; /* hw -> logical axis */
int mapped_btns[3];
u32 irq; /* IRQ number */
struct fasync_struct *async_queue; /* queue for the misc device */
wait_queue_head_t misc_wait; /* Wait queue for the misc device */
unsigned long misc_opened; /* bit0: whether the device is open */
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
struct lis3lv02d_platform_data *pdata; /* for passing board config */
struct mutex mutex; /* Serialize poll and selftest */

View file

@ -29,10 +29,30 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_i2c"
static const char reg_vdd[] = "Vdd";
static const char reg_vdd_io[] = "Vdd_IO";
static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state)
{
int ret;
if (state == LIS3_REG_OFF) {
ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators),
lis3->regulators);
} else {
ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators),
lis3->regulators);
/* Chip needs time to wakeup. Not mentioned in datasheet */
usleep_range(10000, 20000);
}
return ret;
}
static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value)
{
struct i2c_client *c = lis3->bus_priv;
@ -46,24 +66,38 @@ static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v)
return 0;
}
static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len,
u8 *v)
{
struct i2c_client *c = lis3->bus_priv;
reg |= (1 << 7); /* 7th bit enables address auto incrementation */
return i2c_smbus_read_i2c_block_data(c, reg, len, v);
}
static int lis3_i2c_init(struct lis3lv02d *lis3)
{
u8 reg;
int ret;
if (lis3->reg_ctrl)
lis3_reg_ctrl(lis3, LIS3_REG_ON);
lis3->read(lis3, WHO_AM_I, &reg);
if (reg != lis3->whoami)
printk(KERN_ERR "lis3: power on failure\n");
/* power up the device */
ret = lis3->read(lis3, CTRL_REG1, &reg);
if (ret < 0)
return ret;
reg |= CTRL1_PD0;
reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
return lis3->write(lis3, CTRL_REG1, reg);
}
/* Default axis mapping but it can be overwritten by platform data */
static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X,
LIS3_DEV_Y,
LIS3_DEV_Z };
static union axis_conversion lis3lv02d_axis_map =
{ .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@ -72,6 +106,15 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
if (pdata) {
/* Regulator control is optional */
if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL)
lis3_dev.reg_ctrl = lis3_reg_ctrl;
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK)))
lis3_dev.blkread = lis3_i2c_blockread;
if (pdata->axis_x)
lis3lv02d_axis_map.x = pdata->axis_x;
@ -88,6 +131,16 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
goto fail;
}
if (lis3_dev.reg_ctrl) {
lis3_dev.regulators[0].supply = reg_vdd;
lis3_dev.regulators[1].supply = reg_vdd_io;
ret = regulator_bulk_get(&client->dev,
ARRAY_SIZE(lis3_dev.regulators),
lis3_dev.regulators);
if (ret < 0)
goto fail;
}
lis3_dev.pdata = pdata;
lis3_dev.bus_priv = client;
lis3_dev.init = lis3_i2c_init;
@ -95,10 +148,24 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
lis3_dev.write = lis3_i2c_write;
lis3_dev.irq = client->irq;
lis3_dev.ac = lis3lv02d_axis_map;
lis3_dev.pm_dev = &client->dev;
i2c_set_clientdata(client, &lis3_dev);
/* Provide power over the init call */
if (lis3_dev.reg_ctrl)
lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
ret = lis3lv02d_init_device(&lis3_dev);
if (lis3_dev.reg_ctrl)
lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
if (ret == 0)
return 0;
fail:
if (pdata && pdata->release_resources)
pdata->release_resources();
return ret;
}
@ -111,14 +178,18 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
pdata->release_resources();
lis3lv02d_joystick_disable();
lis3lv02d_poweroff(lis3);
lis3lv02d_remove_fs(&lis3_dev);
return lis3lv02d_remove_fs(&lis3_dev);
if (lis3_dev.reg_ctrl)
regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
lis3_dev.regulators);
return 0;
}
#ifdef CONFIG_PM
static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
static int lis3lv02d_i2c_suspend(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
@ -126,18 +197,21 @@ static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
static int lis3lv02d_i2c_resume(struct i2c_client *client)
static int lis3lv02d_i2c_resume(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
/*
* pm_runtime documentation says that devices should always
* be powered on at resume. Pm_runtime turns them off after system
* wide resume is complete.
*/
if (!lis3->pdata || !lis3->pdata->wakeup_flags ||
pm_runtime_suspended(dev))
lis3lv02d_poweron(lis3);
return 0;
}
static void lis3lv02d_i2c_shutdown(struct i2c_client *client)
{
lis3lv02d_i2c_suspend(client, PMSG_SUSPEND);
return 0;
}
#else
#define lis3lv02d_i2c_suspend NULL
@ -145,6 +219,24 @@ static void lis3lv02d_i2c_shutdown(struct i2c_client *client)
#define lis3lv02d_i2c_shutdown NULL
#endif
static int lis3_i2c_runtime_suspend(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
lis3lv02d_poweroff(lis3);
return 0;
}
static int lis3_i2c_runtime_resume(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
lis3lv02d_poweron(lis3);
return 0;
}
static const struct i2c_device_id lis3lv02d_id[] = {
{"lis3lv02d", 0 },
{}
@ -152,14 +244,20 @@ static const struct i2c_device_id lis3lv02d_id[] = {
MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
static const struct dev_pm_ops lis3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend,
lis3lv02d_i2c_resume)
SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend,
lis3_i2c_runtime_resume,
NULL)
};
static struct i2c_driver lis3lv02d_i2c_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3_pm_ops,
},
.suspend = lis3lv02d_i2c_suspend,
.shutdown = lis3lv02d_i2c_shutdown,
.resume = lis3lv02d_i2c_resume,
.probe = lis3lv02d_i2c_probe,
.remove = __devexit_p(lis3lv02d_i2c_remove),
.id_table = lis3lv02d_id,

View file

@ -50,11 +50,12 @@ static int lis3_spi_init(struct lis3lv02d *lis3)
if (ret < 0)
return ret;
reg |= CTRL1_PD0;
reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
return lis3->write(lis3, CTRL_REG1, reg);
}
static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 };
static union axis_conversion lis3lv02d_axis_normal =
{ .as_array = { 1, 2, 3 } };
static int __devinit lis302dl_spi_probe(struct spi_device *spi)
{

315
drivers/hwmon/ltc4261.c Normal file
View file

@ -0,0 +1,315 @@
/*
* Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller
*
* Copyright (C) 2010 Ericsson AB.
*
* Derived from:
*
* Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
* Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
*
* Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
/* chip registers */
#define LTC4261_STATUS 0x00 /* readonly */
#define LTC4261_FAULT 0x01
#define LTC4261_ALERT 0x02
#define LTC4261_CONTROL 0x03
#define LTC4261_SENSE_H 0x04
#define LTC4261_SENSE_L 0x05
#define LTC4261_ADIN2_H 0x06
#define LTC4261_ADIN2_L 0x07
#define LTC4261_ADIN_H 0x08
#define LTC4261_ADIN_L 0x09
/*
* Fault register bits
*/
#define FAULT_OV (1<<0)
#define FAULT_UV (1<<1)
#define FAULT_OC (1<<2)
struct ltc4261_data {
struct device *hwmon_dev;
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
/* Registers */
u8 regs[10];
};
static struct ltc4261_data *ltc4261_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc4261_data *data = i2c_get_clientdata(client);
struct ltc4261_data *ret = data;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) {
int i;
/* Read registers -- 0x00 to 0x09 */
for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
int val;
val = i2c_smbus_read_byte_data(client, i);
if (unlikely(val < 0)) {
dev_dbg(dev,
"Failed to read ADC value: error %d",
val);
ret = ERR_PTR(val);
goto abort;
}
data->regs[i] = val;
}
data->last_updated = jiffies;
data->valid = 1;
}
abort:
mutex_unlock(&data->update_lock);
return ret;
}
/* Return the voltage from the given register in mV or mA */
static int ltc4261_get_value(struct ltc4261_data *data, u8 reg)
{
u32 val;
val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6);
switch (reg) {
case LTC4261_ADIN_H:
case LTC4261_ADIN2_H:
/* 2.5mV resolution. Convert to mV. */
val = val * 25 / 10;
break;
case LTC4261_SENSE_H:
/*
* 62.5uV resolution. Convert to current as measured with
* an 1 mOhm sense resistor, in mA. If a different sense
* resistor is installed, calculate the actual current by
* dividing the reported current by the sense resistor value
* in mOhm.
*/
val = val * 625 / 10;
break;
default:
/* If we get here, the developer messed up */
WARN_ON_ONCE(1);
val = 0;
break;
}
return val;
}
static ssize_t ltc4261_show_value(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ltc4261_data *data = ltc4261_update_device(dev);
int value;
if (IS_ERR(data))
return PTR_ERR(data);
value = ltc4261_get_value(data, attr->index);
return snprintf(buf, PAGE_SIZE, "%d\n", value);
}
static ssize_t ltc4261_show_bool(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
struct ltc4261_data *data = ltc4261_update_device(dev);
u8 fault;
if (IS_ERR(data))
return PTR_ERR(data);
fault = data->regs[LTC4261_FAULT] & attr->index;
if (fault) /* Clear reported faults in chip register */
i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault);
return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0);
}
/*
* These macros are used below in constructing device attribute objects
* for use with sysfs_create_group() to make a sysfs device file
* for each register.
*/
#define LTC4261_VALUE(name, ltc4261_cmd_idx) \
static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
ltc4261_show_value, NULL, ltc4261_cmd_idx)
#define LTC4261_BOOL(name, mask) \
static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
ltc4261_show_bool, NULL, (mask))
/*
* Input voltages.
*/
LTC4261_VALUE(in1_input, LTC4261_ADIN_H);
LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
/*
* Voltage alarms. The chip has only one set of voltage alarm status bits,
* triggered by input voltage alarms. In many designs, those alarms are
* associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin
* to the OV pin. ADIN2 is, however, not available on all chip variants.
* To ensure that the alarm condition is reported to the user, report it
* with both voltage sensors.
*/
LTC4261_BOOL(in1_min_alarm, FAULT_UV);
LTC4261_BOOL(in1_max_alarm, FAULT_OV);
LTC4261_BOOL(in2_min_alarm, FAULT_UV);
LTC4261_BOOL(in2_max_alarm, FAULT_OV);
/* Currents (via sense resistor) */
LTC4261_VALUE(curr1_input, LTC4261_SENSE_H);
/* Overcurrent alarm */
LTC4261_BOOL(curr1_max_alarm, FAULT_OC);
static struct attribute *ltc4261_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in2_min_alarm.dev_attr.attr,
&sensor_dev_attr_in2_max_alarm.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
NULL,
};
static const struct attribute_group ltc4261_group = {
.attrs = ltc4261_attributes,
};
static int ltc4261_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct ltc4261_data *data;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) {
dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n",
adapter->id, client->addr, LTC4261_STATUS);
return -ENODEV;
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto out_kzalloc;
}
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Clear faults */
i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00);
/* Register sysfs hooks */
ret = sysfs_create_group(&client->dev.kobj, &ltc4261_group);
if (ret)
goto out_sysfs_create_group;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
goto out_hwmon_device_register;
}
return 0;
out_hwmon_device_register:
sysfs_remove_group(&client->dev.kobj, &ltc4261_group);
out_sysfs_create_group:
kfree(data);
out_kzalloc:
return ret;
}
static int ltc4261_remove(struct i2c_client *client)
{
struct ltc4261_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ltc4261_group);
kfree(data);
return 0;
}
static const struct i2c_device_id ltc4261_id[] = {
{"ltc4261", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ltc4261_id);
/* This is the driver that will be inserted */
static struct i2c_driver ltc4261_driver = {
.driver = {
.name = "ltc4261",
},
.probe = ltc4261_probe,
.remove = ltc4261_remove,
.id_table = ltc4261_id,
};
static int __init ltc4261_init(void)
{
return i2c_add_driver(&ltc4261_driver);
}
static void __exit ltc4261_exit(void)
{
i2c_del_driver(&ltc4261_driver);
}
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
MODULE_DESCRIPTION("LTC4261 driver");
MODULE_LICENSE("GPL");
module_init(ltc4261_init);
module_exit(ltc4261_exit);

View file

@ -21,7 +21,6 @@
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
@ -35,6 +34,7 @@
#include <linux/cpu.h>
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/smp.h>
#define DRVNAME "pkgtemp"
@ -339,8 +339,7 @@ static int __cpuinit pkgtemp_device_add(unsigned int cpu)
return err;
}
#ifdef CONFIG_HOTPLUG_CPU
static void pkgtemp_device_remove(unsigned int cpu)
static void __cpuinit pkgtemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p;
unsigned int i;
@ -387,12 +386,10 @@ static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb,
static struct notifier_block pkgtemp_cpu_notifier __refdata = {
.notifier_call = pkgtemp_cpu_callback,
};
#endif /* !CONFIG_HOTPLUG_CPU */
static int __init pkgtemp_init(void)
{
int i, err = -ENODEV;
struct pdev_entry *p, *n;
/* quick check if we run Intel */
if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
@ -402,31 +399,23 @@ static int __init pkgtemp_init(void)
if (err)
goto exit;
for_each_online_cpu(i) {
err = pkgtemp_device_add(i);
if (err)
goto exit_devices_unreg;
}
for_each_online_cpu(i)
pkgtemp_device_add(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
err = -ENODEV;
goto exit_driver_unreg;
}
#ifdef CONFIG_HOTPLUG_CPU
register_hotcpu_notifier(&pkgtemp_cpu_notifier);
#endif
register_hotcpu_notifier(&pkgtemp_cpu_notifier);
return 0;
exit_devices_unreg:
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
#ifndef CONFIG_HOTPLUG_CPU
exit_driver_unreg:
platform_driver_unregister(&pkgtemp_driver);
#endif
exit:
return err;
}
@ -434,9 +423,8 @@ static int __init pkgtemp_init(void)
static void __exit pkgtemp_exit(void)
{
struct pdev_entry *p, *n;
#ifdef CONFIG_HOTPLUG_CPU
unregister_hotcpu_notifier(&pkgtemp_cpu_notifier);
#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);

View file

@ -22,10 +22,8 @@
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
@ -237,8 +235,7 @@ static int __cpuinit via_cputemp_device_add(unsigned int cpu)
return err;
}
#ifdef CONFIG_HOTPLUG_CPU
static void via_cputemp_device_remove(unsigned int cpu)
static void __cpuinit via_cputemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p, *n;
mutex_lock(&pdev_list_mutex);
@ -272,7 +269,6 @@ static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb,
static struct notifier_block via_cputemp_cpu_notifier __refdata = {
.notifier_call = via_cputemp_cpu_callback,
};
#endif /* !CONFIG_HOTPLUG_CPU */
static int __init via_cputemp_init(void)
{
@ -313,9 +309,7 @@ static int __init via_cputemp_init(void)
goto exit_driver_unreg;
}
#ifdef CONFIG_HOTPLUG_CPU
register_hotcpu_notifier(&via_cputemp_cpu_notifier);
#endif
return 0;
exit_devices_unreg:
@ -335,9 +329,8 @@ static int __init via_cputemp_init(void)
static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
#ifdef CONFIG_HOTPLUG_CPU
unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);

36
include/linux/gpio-fan.h Normal file
View file

@ -0,0 +1,36 @@
/*
* include/linux/gpio-fan.h
*
* Platform data structure for GPIO fan driver
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __LINUX_GPIO_FAN_H
#define __LINUX_GPIO_FAN_H
struct gpio_fan_alarm {
unsigned gpio;
unsigned active_low;
};
struct gpio_fan_speed {
int rpm;
int ctrl_val;
};
struct gpio_fan_platform_data {
int num_ctrl;
unsigned *ctrl; /* fan control GPIOs. */
struct gpio_fan_alarm *alarm; /* fan alarm GPIO. */
/*
* Speed conversion array: rpm from/to GPIO bit field.
* This array _must_ be sorted in ascending rpm order.
*/
int num_speed;
struct gpio_fan_speed *speed;
};
#endif /* __LINUX_GPIO_FAN_H */

View file

@ -1,6 +1,52 @@
#ifndef __LIS3LV02D_H_
#define __LIS3LV02D_H_
/**
* struct lis3lv02d_platform_data - lis3 chip family platform data
* @click_flags: Click detection unit configuration
* @click_thresh_x: Click detection unit x axis threshold
* @click_thresh_y: Click detection unit y axis threshold
* @click_thresh_z: Click detection unit z axis threshold
* @click_time_limit: Click detection unit time parameter
* @click_latency: Click detection unit latency parameter
* @click_window: Click detection unit window parameter
* @irq_cfg: On chip irq source and type configuration (click /
* data available / wake up, open drain, polarity)
* @irq_flags1: Additional irq triggering flags for irq channel 0
* @irq_flags2: Additional irq triggering flags for irq channel 1
* @duration1: Wake up unit 1 duration parameter
* @duration2: Wake up unit 2 duration parameter
* @wakeup_flags: Wake up unit 1 flags
* @wakeup_thresh: Wake up unit 1 threshold value
* @wakeup_flags2: Wake up unit 2 flags
* @wakeup_thresh2: Wake up unit 2 threshold value
* @hipass_ctrl: High pass filter control (enable / disable, cut off
* frequency)
* @axis_x: Sensor orientation remapping for x-axis
* @axis_y: Sensor orientation remapping for y-axis
* @axis_z: Sensor orientation remapping for z-axis
* @driver_features: Enable bits for different features. Disabled by default
* @default_rate: Default sampling rate. 0 means reset default
* @setup_resources: Interrupt line setup call back function
* @release_resources: Interrupt line release call back function
* @st_min_limits[3]: Selftest acceptance minimum values
* @st_max_limits[3]: Selftest acceptance maximum values
* @irq2: Irq line 2 number
*
* Platform data is used to setup the sensor chip. Meaning of the different
* chip features can be found from the data sheet. It is publicly available
* at www.st.com web pages. Currently the platform data is used
* only for the 8 bit device. The 8 bit device has two wake up / free fall
* detection units and click detection unit. There are plenty of ways to
* configure the chip which makes is quite hard to explain deeper meaning of
* the fields here. Behaviour of the detection blocks varies heavily depending
* on the configuration. For example, interrupt detection block can use high
* pass filtered data which makes it react to the changes in the acceleration.
* Irq_flags can be used to enable interrupt detection on the both edges.
* With proper chip configuration this produces interrupt when some trigger
* starts and when it goes away.
*/
struct lis3lv02d_platform_data {
/* please note: the 'click' feature is only supported for
* LIS[32]02DL variants of the chip and will be ignored for
@ -36,7 +82,10 @@ struct lis3lv02d_platform_data {
#define LIS3_IRQ_OPEN_DRAIN (1 << 6)
#define LIS3_IRQ_ACTIVE_LOW (1 << 7)
unsigned char irq_cfg;
unsigned char irq_flags1; /* Additional irq edge / level flags */
unsigned char irq_flags2; /* Additional irq edge / level flags */
unsigned char duration1;
unsigned char duration2;
#define LIS3_WAKEUP_X_LO (1 << 0)
#define LIS3_WAKEUP_X_HI (1 << 1)
#define LIS3_WAKEUP_Y_LO (1 << 2)
@ -64,6 +113,10 @@ struct lis3lv02d_platform_data {
s8 axis_x;
s8 axis_y;
s8 axis_z;
#define LIS3_USE_REGULATOR_CTRL 0x01
#define LIS3_USE_BLOCK_READ 0x02
u16 driver_features;
int default_rate;
int (*setup_resources)(void);
int (*release_resources)(void);
/* Limits for selftest are specified in chip data sheet */