Commit bbc7610c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6

* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6:
  hwmon: Add MAINTAINERS entry for new ams driver
  hwmon: New AMS hardware monitoring driver
  hwmon/w83793: Add documentation and maintainer
  hwmon: New Winbond W83793 hardware monitoring driver
  hwmon: Update Rudolf Marek's e-mail address
  hwmon/f71805f: Fix the device address decoding
  hwmon/f71805f: Always create all fan inputs
  hwmon/f71805f: Add support for the Fintek F71872F/FG chip
  hwmon: New PC87427 hardware monitoring driver
  hwmon/it87: Remove the SMBus interface support
  hwmon/hdaps: Update the list of supported devices
  hwmon/hdaps: Move the DMI detection data to .data
  hwmon/pc87360: Autodetect the VRM version
  hwmon/f71805f: Document the fan control features
  hwmon/f71805f: Add support for "speed mode" fan speed control
  hwmon/f71805f: Support DC fan speed control mode
  hwmon/f71805f: Let the user adjust the PWM base frequency
  hwmon/f71805f: Add manual fan speed control
  hwmon/f71805f: Store the fan control registers
parents 5cbded58 284f42b6
......@@ -151,15 +151,6 @@ Who: Thomas Gleixner <tglx@linutronix.de>
---------------------------
What: I2C interface of the it87 driver
When: January 2007
Why: The ISA interface is faster and should be always available. The I2C
probing is also known to cause trouble in at least one case (see
bug #5889.)
Who: Jean Delvare <khali@linux-fr.org>
---------------------------
What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports
(temporary transition config option provided until then)
The transition config option will also be removed at the same time.
......
......@@ -6,6 +6,10 @@ Supported chips:
Prefix: 'f71805f'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Provided by Fintek on request
* Fintek F71872F/FG
Prefix: 'f71872f'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Provided by Fintek on request
Author: Jean Delvare <khali@linux-fr.org>
......@@ -13,8 +17,8 @@ Thanks to Denis Kieft from Barracuda Networks for the donation of a
test system (custom Jetway K8M8MS motherboard, with CPU and RAM) and
for providing initial documentation.
Thanks to Kris Chen from Fintek for answering technical questions and
providing additional documentation.
Thanks to Kris Chen and Aaron Huang from Fintek for answering technical
questions and providing additional documentation.
Thanks to Chris Lin from Jetway for providing wiring schematics and
answering technical questions.
......@@ -28,8 +32,11 @@ capabilities. It can monitor up to 9 voltages (counting its own power
source), 3 fans and 3 temperature sensors.
This chip also has fan controlling features, using either DC or PWM, in
three different modes (one manual, two automatic). The driver doesn't
support these features yet.
three different modes (one manual, two automatic).
The Fintek F71872F/FG Super I/O chip is almost the same, with two
additional internal voltages monitored (VSB and battery). It also features
6 VID inputs. The VID inputs are not yet supported by this driver.
The driver assumes that no more than one chip is present, which seems
reasonable.
......@@ -42,7 +49,8 @@ Voltages are sampled by an 8-bit ADC with a LSB of 8 mV. The supported
range is thus from 0 to 2.040 V. Voltage values outside of this range
need external resistors. An exception is in0, which is used to monitor
the chip's own power source (+3.3V), and is divided internally by a
factor 2.
factor 2. For the F71872F/FG, in9 (VSB) and in10 (battery) are also
divided internally by a factor 2.
The two LSB of the voltage limit registers are not used (always 0), so
you can only set the limits in steps of 32 mV (before scaling).
......@@ -61,9 +69,12 @@ in5 VIN5 +12V 200K 20K 11.00 1.05 V
in6 VIN6 VCC1.5V 10K - 1.00 1.50 V
in7 VIN7 VCORE 10K - 1.00 ~1.40 V (1)
in8 VIN8 VSB5V 200K 47K 1.00 0.95 V
in10 VSB VSB3.3V int. int. 2.00 1.65 V (3)
in9 VBAT VBATTERY int. int. 2.00 1.50 V (3)
(1) Depends on your hardware setup.
(2) Obviously not correct, swapping R1 and R2 would make more sense.
(3) F71872F/FG only.
These values can be used as hints at best, as motherboard manufacturers
are free to use a completely different setup. As a matter of fact, the
......@@ -103,3 +114,38 @@ sensor. Each channel can be used for connecting either a thermal diode
or a thermistor. The driver reports the currently selected mode, but
doesn't allow changing it. In theory, the BIOS should have configured
everything properly.
Fan Control
-----------
Both PWM (pulse-width modulation) and DC fan speed control methods are
supported. The right one to use depends on external circuitry on the
motherboard, so the driver assumes that the BIOS set the method
properly. The driver will report the method, but won't let you change
it.
When the PWM method is used, you can select the operating frequency,
from 187.5 kHz (default) to 31 Hz. The best frequency depends on the
fan model. As a rule of thumb, lower frequencies seem to give better
control, but may generate annoying high-pitch noise. Fintek recommends
not going below 1 kHz, as the fan tachometers get confused by lower
frequencies as well.
When the DC method is used, Fintek recommends not going below 5 V, which
corresponds to a pwm value of 106 for the driver. The driver doesn't
enforce this limit though.
Three different fan control modes are supported:
* Manual mode
You ask for a specific PWM duty cycle or DC voltage.
* Fan speed mode
You ask for a specific fan speed. This mode assumes that pwm1
corresponds to fan1, pwm2 to fan2 and pwm3 to fan3.
* Temperature mode
You define 3 temperature/fan speed trip points, and the fan speed is
adjusted depending on the measured temperature, using interpolation.
This mode is not yet supported by the driver.
......@@ -9,8 +9,7 @@ Supported chips:
http://www.ite.com.tw/
* IT8712F
Prefix: 'it8712'
Addresses scanned: I2C 0x2d
from Super I/O config space (8 I/O ports)
Addresses scanned: from Super I/O config space (8 I/O ports)
Datasheet: Publicly available at the ITE website
http://www.ite.com.tw/
* IT8716F
......@@ -53,6 +52,18 @@ Module Parameters
misconfigured by BIOS - PWM values would be inverted. This option tries
to fix this. Please contact your BIOS manufacturer and ask him for fix.
Hardware Interfaces
-------------------
All the chips suported by this driver are LPC Super-I/O chips, accessed
through the LPC bus (ISA-like I/O ports). The IT8712F additionally has an
SMBus interface to the hardware monitoring functions. This driver no
longer supports this interface though, as it is slower and less reliable
than the ISA access, and was only available on a small number of
motherboard models.
Description
-----------
......
......@@ -8,7 +8,7 @@ Supported chips:
Datasheet: http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
Author: Rudolf Marek
Contact: Rudolf Marek <r.marek@sh.cvut.cz>
Contact: Rudolf Marek <r.marek@assembler.cz>
Description
-----------
......
Kernel driver pc87427
=====================
Supported chips:
* National Semiconductor PC87427
Prefix: 'pc87427'
Addresses scanned: none, address read from Super I/O config space
Datasheet: http://www.winbond.com.tw/E-WINBONDHTM/partner/apc_007.html
Author: Jean Delvare <khali@linux-fr.org>
Thanks to Amir Habibi at Candelis for setting up a test system, and to
Michael Kress for testing several iterations of this driver.
Description
-----------
The National Semiconductor Super I/O chip includes complete hardware
monitoring capabilities. It can monitor up to 18 voltages, 8 fans and
6 temperature sensors. Only the fans are supported at the moment.
This chip also has fan controlling features, which are not yet supported
by this driver either.
The driver assumes that no more than one chip is present, which seems
reasonable.
Fan Monitoring
--------------
Fan rotation speeds are reported as 14-bit values from a gated clock
signal. Speeds down to 83 RPM can be measured.
An alarm is triggered if the rotation speed drops below a programmable
limit. Another alarm is triggered if the speed is too low to to be measured
(including stalled or missing fan).
......@@ -208,12 +208,14 @@ temp[1-*]_auto_point[1-*]_temp_hyst
****************
temp[1-*]_type Sensor type selection.
Integers 1 to 4 or thermistor Beta value (typically 3435)
Integers 1 to 6 or thermistor Beta value (typically 3435)
RW
1: PII/Celeron Diode
2: 3904 transistor
3: thermal diode
4: thermistor (default/unknown Beta)
5: AMD AMDSI
6: Intel PECI
Not all types are supported by all chips
temp[1-*]_max Temperature max value.
......
......@@ -10,7 +10,7 @@ Supported chips:
Authors:
Jean Delvare <khali@linux-fr.org>
Yuan Mu (Winbond)
Rudolf Marek <r.marek@sh.cvut.cz>
Rudolf Marek <r.marek@assembler.cz>
Description
-----------
......
......@@ -18,7 +18,7 @@ Credits:
and Mark Studebaker <mdsxyz123@yahoo.com>
w83792d.c:
Chunhao Huang <DZShen@Winbond.com.tw>,
Rudolf Marek <r.marek@sh.cvut.cz>
Rudolf Marek <r.marek@assembler.cz>
Additional contributors:
Sven Anders <anders@anduras.de>
......
Kernel driver w83793
====================
Supported chips:
* Winbond W83793G/W83793R
Prefix: 'w83793'
Addresses scanned: I2C 0x2c - 0x2f
Datasheet: Still not published
Authors:
Yuan Mu (Winbond Electronics)
Rudolf Marek <r.marek@assembler.cz>
Module parameters
-----------------
* reset int
(default 0)
This parameter is not recommended, it will lose motherboard specific
settings. Use 'reset=1' to reset the chip when loading this module.
* force_subclients=bus,caddr,saddr1,saddr2
This is used to force the i2c addresses for subclients of
a certain chip. Typical usage is `force_subclients=0,0x2f,0x4a,0x4b'
to force the subclients of chip 0x2f on bus 0 to i2c addresses
0x4a and 0x4b.
Description
-----------
This driver implements support for Winbond W83793G/W83793R chips.
* Exported features
This driver exports 10 voltage sensors, up to 12 fan tachometer inputs,
6 remote temperatures, up to 8 sets of PWM fan controls, SmartFan
(automatic fan speed control) on all temperature/PWM combinations, 2
sets of 6-pin CPU VID input.
* Sensor resolutions
If your motherboard maker used the reference design, the resolution of
voltage0-2 is 2mV, resolution of voltage3/4/5 is 16mV, 8mV for voltage6,
24mV for voltage7/8. Temp1-4 have a 0.25 degree Celsius resolution,
temp5-6 have a 1 degree Celsiis resolution.
* Temperature sensor types
Temp1-4 have 3 possible types. It can be read from (and written to)
temp[1-4]_type.
- If the value of 0, the related temperature channel stops
monitoring.
- If the value is 3, it starts monitoring using a remote termal diode
(default).
- If the value is 5, it starts monitoring using the temperature sensor
in AMD CPU and get result by AMDSI.
- If the value is 6, it starts monitoring using the temperature sensor
in Intel CPU and get result by PECI.
Temp5-6 can be connected to external thermistors (value of
temp[5-6]_type is 4). They can also be disabled (value is 0).
* Alarm mechanism
For voltage sensors, an alarm triggers if the measured value is below
the low voltage limit or over the high voltage limit.
For temperature sensors, an alarm triggers if the measured value goes
above the high temperature limit, and wears off only after the measured
value drops below the hysteresis value.
For fan sensors, an alarm triggers if the measured value is below the
low speed limit.
* SmartFan/PWM control
If you want to set a pwm fan to manual mode, you just need to make sure it
is not controlled by any temp channel, for example, you want to set fan1
to manual mode, you need to check the value of temp[1-6]_fan_map, make
sure bit 0 is cleared in the 6 values. And then set the pwm1 value to
control the fan.
Each temperature channel can control all the 8 PWM outputs (by setting the
corresponding bit in tempX_fan_map), you can set the temperature channel
mode using temp[1-6]_pwm_enable, 2 is Thermal Cruise mode and 3
is the SmartFanII mode. Temperature channels will try to speed up or
slow down all controlled fans, this means one fan can receive different
PWM value requests from different temperature channels, but the chip
will always pick the safest (max) PWM value for each fan.
In Thermal Cruise mode, the chip attempts to keep the temperature at a
predefined value, within a tolerance margin. So if tempX_input >
thermal_cruiseX + toleranceX, the chip will increase the PWM value,
if tempX_input < thermal_cruiseX - toleranceX, the chip will decrease
the PWM value. If the temperature is within the tolerance range, the PWM
value is left unchanged.
SmartFanII works differently, you have to define up to 7 PWM, temperature
trip points, defining a PWM/temperature curve which the chip will follow.
While not fundamentally different from the Thermal Cruise mode, the
implementation is quite different, giving you a finer-grained control.
* Chassis
If the case open alarm triggers, it will stay in this state unless cleared
by any write to the sysfs file "chassis".
* VID and VRM
The VRM version is detected automatically, don't modify the it unless you
*do* know the cpu VRM version and it's not properly detected.
Notes
-----
Only Fan1-5 and PWM1-3 are guaranteed to always exist, other fan inputs and
PWM outputs may or may not exist depending on the chip pin configuration.
......@@ -277,7 +277,7 @@ S: Maintained
ALI1563 I2C DRIVER
P: Rudolf Marek
M: r.marek@sh.cvut.cz
M: r.marek@assembler.cz
L: i2c@lm-sensors.org
S: Maintained
......@@ -296,6 +296,13 @@ L: info-linux@geode.amd.com
W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html
S: Supported
AMS (Apple Motion Sensor) DRIVER
P: Stelian Pop
M: stelian@popies.net
P: Michael Hanselmann
M: linux-kernel@hansmi.ch
S: Supported
AMSO1100 RNIC DRIVER
P: Tom Tucker
M: tom@opengridcomputing.com
......@@ -3436,6 +3443,12 @@ M: bezaur@gmail.com
L: lm-sensors@lm-sensors.org
S: Maintained
W83793 HARDWARE MONITORING DRIVER
P: Rudolf Marek
M: r.marek@assembler.cz
L: lm-sensors@lm-sensors.org
S: Maintained
W83L51xD SD/MMC CARD INTERFACE DRIVER
P: Pierre Ossman
M: drzeus-wbsd@drzeus.cx
......
......@@ -106,6 +106,31 @@ config SENSORS_K8TEMP
This driver can also be built as a module. If so, the module
will be called k8temp.
config SENSORS_AMS
tristate "Apple Motion Sensor driver"
depends on HWMON && PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
help
Support for the motion sensor included in PowerBooks. Includes
implementations for PMU and I2C.
This driver can also be built as a module. If so, the module
will be called ams.
config SENSORS_AMS_PMU
bool "PMU variant"
depends on SENSORS_AMS && ADB_PMU
default y
help
PMU variant of motion sensor, found in late 2005 PowerBooks.
config SENSORS_AMS_I2C
bool "I2C variant"
depends on SENSORS_AMS && I2C
default y
help
I2C variant of motion sensor, found in early 2005 PowerBooks and
iBooks.
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on HWMON && I2C && EXPERIMENTAL
......@@ -142,11 +167,12 @@ config SENSORS_DS1621
will be called ds1621.
config SENSORS_F71805F
tristate "Fintek F71805F/FG"
tristate "Fintek F71805F/FG and F71872F/FG"
depends on HWMON && EXPERIMENTAL
help
If you say yes here you get support for hardware monitoring
features of the Fintek F71805F/FG chips.
features of the Fintek F71805F/FG and F71872F/FG Super-I/O
chips.
This driver can also be built as a module. If so, the module
will be called f71805f.
......@@ -353,6 +379,19 @@ config SENSORS_PC87360
This driver can also be built as a module. If so, the module
will be called pc87360.
config SENSORS_PC87427
tristate "National Semiconductor PC87427"
depends on HWMON && EXPERIMENTAL
help
If you say yes here you get access to the hardware monitoring
functions of the National Semiconductor PC87427 Super-I/O chip.
The chip has two distinct logical devices, one for fan speed
monitoring and control, and one for voltage and temperature
monitoring. Only fan speed monitoring is supported right now.
This driver can also be built as a module. If so, the module
will be called pc87427.
config SENSORS_SIS5595
tristate "Silicon Integrated Systems Corp. SiS5595"
depends on HWMON && I2C && PCI && EXPERIMENTAL
......@@ -474,6 +513,16 @@ config SENSORS_W83792D
This driver can also be built as a module. If so, the module
will be called w83792d.
config SENSORS_W83793
tristate "Winbond W83793"
depends on HWMON && I2C && EXPERIMENTAL
help
If you say yes here you get support for the Winbond W83793
hardware monitoring chip.
This driver can also be built as a module. If so, the module
will be called w83793.
config SENSORS_W83L785TS
tristate "Winbond W83L785TS-S"
depends on HWMON && I2C && EXPERIMENTAL
......@@ -527,6 +576,9 @@ config SENSORS_HDAPS
This driver also provides an absolute input class device, allowing
the laptop to act as a pinball machine-esque joystick.
If your ThinkPad is not recognized by the driver, please update to latest
BIOS. This is especially the case for some R52 ThinkPads.
Say Y here if you have an applicable laptop and want to experience
the awesome power of hdaps.
......
......@@ -9,6 +9,7 @@ obj-$(CONFIG_HWMON_VID) += hwmon-vid.o
obj-$(CONFIG_SENSORS_ASB100) += asb100.o
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
obj-$(CONFIG_SENSORS_W83793) += w83793.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
......@@ -18,6 +19,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_AMS) += ams/
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
......@@ -41,6 +43,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
......
#
# Makefile for Apple Motion Sensor driver
#
ams-y := ams-core.o ams-input.o
ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o
ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o
obj-$(CONFIG_SENSORS_AMS) += ams.o
/*
* Apple Motion Sensor driver
*
* Copyright (C) 2005 Stelian Pop (stelian@popies.net)
* Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/pmac_pfunc.h>
#include <asm/of_platform.h>
#include "ams.h"
/* There is only one motion sensor per machine */
struct ams ams_info;
static unsigned int verbose;
module_param(verbose, bool, 0644);
MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output");
/* Call with ams_info.lock held! */
void ams_sensors(s8 *x, s8 *y, s8 *z)
{
u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2;
if (orient & 0x80)
/* X and Y swapped */
ams_info.get_xyz(y, x, z);
else
ams_info.get_xyz(x, y, z);
if (orient & 0x04)
*z = ~(*z);
if (orient & 0x02)
*y = ~(*y);
if (orient & 0x01)
*x = ~(*x);
}
static ssize_t ams_show_current(struct device *dev,
struct device_attribute *attr, char *buf)
{
s8 x, y, z;
mutex_lock(&ams_info.lock);
ams_sensors(&x, &y, &z);
mutex_unlock(&ams_info.lock);
return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z);
}
static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL);
static void ams_handle_irq(void *data)
{
enum ams_irq irq = *((enum ams_irq *)data);
spin_lock(&ams_info.irq_lock);
ams_info.worker_irqs |= irq;
schedule_work(&ams_info.worker);
spin_unlock(&ams_info.irq_lock);
}
static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL;
static struct pmf_irq_client ams_freefall_client = {
.owner = THIS_MODULE,
.handler = ams_handle_irq,
.data = &ams_freefall_irq_data,
};
static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK;
static struct pmf_irq_client ams_shock_client = {
.owner = THIS_MODULE,
.handler = ams_handle_irq,
.data = &ams_shock_irq_data,
};
/* Once hard disk parking is implemented in the kernel, this function can
* trigger it.
*/
static void ams_worker(struct work_struct *work)
{
mutex_lock(&ams_info.lock);
if (ams_info.has_device) {
unsigned long flags;
spin_lock_irqsave(&ams_info.irq_lock, flags);
if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) {
if (verbose)
printk(KERN_INFO "ams: freefall detected!\n");
ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL;
/* we must call this with interrupts enabled */
spin_unlock_irqrestore(&ams_info.irq_lock, flags);
ams_info.clear_irq(AMS_IRQ_FREEFALL);
spin_lock_irqsave(&ams_info.irq_lock, flags);
}
if (ams_info.worker_irqs & AMS_IRQ_SHOCK) {
if (verbose)
printk(KERN_INFO "ams: shock detected!\n");
ams_info.worker_irqs &= ~AMS_IRQ_SHOCK;
/* we must call this with interrupts enabled */
spin_unlock_irqrestore(&ams_info.irq_lock, flags);
ams_info.clear_irq(AMS_IRQ_SHOCK);
spin_lock_irqsave(&ams_info.irq_lock, flags);
}
spin_unlock_irqrestore(&ams_info.irq_lock, flags);
}
mutex_unlock(&ams_info.lock);
}
/* Call with ams_info.lock held! */
int ams_sensor_attach(void)
{
int result;
u32 *prop;
/* Get orientation */
prop = (u32*)get_property(ams_info.of_node, "orientation", NULL);
if (!prop)
return -ENODEV;
ams_info.orient1 = *prop;
ams_info.orient2 = *(prop + 1);
/* Register freefall interrupt handler */
result = pmf_register_irq_client(ams_info.of_node,
"accel-int-1",
&ams_freefall_client);
if (result < 0)
return -ENODEV;
/* Reset saved irqs */
ams_info.worker_irqs = 0;
/* Register shock interrupt handler */
result = pmf_register_irq_client(ams_info.of_node,
"accel-int-2",
&ams_shock_client);
if (result < 0)
goto release_freefall;
/* Create device */
ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL);
if (!ams_info.of_dev) {
result = -ENODEV;
goto release_shock;
}
/* Create attributes */
result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current);
if (result)
goto release_of;
ams_info.vflag = !!(ams_info.get_vendor() & 0x10);
/* Init input device */
result = ams_input_init();
if (result)
goto release_device_file;
return result;
release_device_file:
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
release_of:
of_device_unregister(ams_info.of_dev);
release_shock:
pmf_unregister_irq_client(&ams_shock_client);
release_freefall:
pmf_unregister_irq_client(&ams_freefall_client);
return result;
}
int __init ams_init(void)
{
struct device_node *np;
spin_lock_init(&ams_info.irq_lock);
mutex_init(&ams_info.lock);
INIT_WORK(&ams_info.worker, ams_worker);
#ifdef CONFIG_SENSORS_AMS_I2C
np = of_find_node_by_name(NULL, "accelerometer");
if (np && device_is_compatible(np, "AAPL,accelerometer_1"))
/* Found I2C motion sensor */
return ams_i2c_init(np);
#endif
#ifdef CONFIG_SENSORS_AMS_PMU
np = of_find_node_by_name(NULL, "sms");
if (np && device_is_compatible(np, "sms"))
/* Found PMU motion sensor */
return ams_pmu_init(np);
#endif
printk(KERN_ERR "ams: No motion sensor found.\n");
return -ENODEV;
}
void ams_exit(void)
{
mutex_lock(&ams_info.lock);
if (ams_info.has_device) {
/* Remove input device */
ams_input_exit();
/* Shut down implementation */
ams_info.exit();
/* Flush interrupt worker
*
* We do this after ams_info.exit(), because an interrupt might
* have arrived before disabling them.
*/
flush_scheduled_work();
/* Remove attributes */
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
/* Remove device */
of_device_unregister(ams_info.of_dev);
/* Remove handler */
pmf_unregister_irq_client(&ams_shock_client);
pmf_unregister_irq_client(&ams_freefall_client);
}
mutex_unlock(&ams_info.lock);
}
MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
MODULE_DESCRIPTION("Apple Motion Sensor driver");
MODULE_LICENSE("GPL");
module_init(ams_init);
module_exit(ams_exit);
/*
* Apple Motion Sensor driver (I2C variant)
*
* Copyright (C) 2005 Stelian Pop (stelian@popies.net)
* Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
*
* Clean room implementation based on the reverse engineered Mac OS X driver by
* Johannes Berg <johannes@sipsolutions.net>, documentation available at
* http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification
*
* 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.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include "ams.h"
/* AMS registers */
#define AMS_COMMAND 0x00 /* command register */
#define AMS_STATUS 0x01 /* status register */
#define AMS_CTRL1 0x02 /* read control 1 (number of values) */
#define AMS_CTRL2 0x03 /* read control 2 (offset?) */
#define AMS_CTRL3 0x04 /* read control 3 (size of each value?) */
#define AMS_DATA1 0x05 /* read data 1 */
#define AMS_DATA2 0x06 /* read data 2 */
#define AMS_DATA3 0x07 /* read data 3 */
#define AMS_DATA4 0x08 /* read data 4 */
#define AMS_DATAX 0x20 /* data X */
#define AMS_DATAY 0x21 /* data Y */
#define AMS_DATAZ 0x22 /* data Z */
#define AMS_FREEFALL 0x24 /* freefall int control */
#define AMS_SHOCK 0x25 /* shock int control */
#define AMS_SENSLOW 0x26 /* sensitivity low limit */
#define AMS_SENSHIGH 0x27 /* sensitivity high limit */
#define AMS_CTRLX 0x28 /* control X */
#define AMS_CTRLY 0x29 /* control Y */
#define AMS_CTRLZ 0x2A /* control Z */
#define AMS_UNKNOWN1 0x2B /* unknown 1 */
#define AMS_UNKNOWN2 0x2C /* unknown 2 */
#define AMS_UNKNOWN3 0x2D /* unknown 3 */
#define AMS_VENDOR 0x2E /* vendor */
/* AMS commands - use with the AMS_COMMAND register */
enum ams_i2c_cmd {
AMS_CMD_NOOP = 0,
AMS_CMD_VERSION,
AMS_CMD_READMEM,
AMS_CMD_WRITEMEM,
AMS_CMD_ERASEMEM,
AMS_CMD_READEE,
AMS_CMD_WRITEEE,
AMS_CMD_RESET,
AMS_CMD_START,
};
static int ams_i2c_attach(struct i2c_adapter *adapter);
static int ams_i2c_detach(struct i2c_adapter *adapter);
static struct i2c_driver ams_i2c_driver = {
.driver = {
.name = "ams",
.owner = THIS_MODULE,
},
.attach_adapter = ams_i2c_attach,
.detach_adapter = ams_i2c_detach,
};
static s32 ams_i2c_read(u8 reg)
{
return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg);
}
static int ams_i2c_write(u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value);
}
static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
{
s32 result;
int remaining = HZ / 20;
ams_i2c_write(AMS_COMMAND, cmd);
mdelay(5);
while (remaining) {
result = ams_i2c_read(AMS_COMMAND);
if (result == 0 || result & 0x80)
return 0;
remaining = schedule_timeout(remaining);
}
return -1;
}
static void ams_i2c_set_irq(enum ams_irq reg, char enable)
{
if (reg & AMS_IRQ_FREEFALL) {
u8 val = ams_i2c_read(AMS_CTRLX);
if (enable)
val |= 0x80;
else
val &= ~0x80;
ams_i2c_write(AMS_CTRLX, val);
}
if (reg & AMS_IRQ_SHOCK) {
u8 val = ams_i2c_read(AMS_CTRLY);
if (enable)
val |= 0x80;
else
val &= ~0x80;
ams_i2c_write(AMS_CTRLY, val);
}
if (reg & AMS_IRQ_GLOBAL) {
u8 val = ams_i2c_read(AMS_CTRLZ);
if (enable)
val |= 0x80;
else
val &= ~0x80;
ams_i2c_write(AMS_CTRLZ, val);
}
}
static void ams_i2c_clear_irq(enum ams_irq reg)
{
if (reg & AMS_IRQ_FREEFALL)
ams_i2c_write(AMS_FREEFALL, 0);
if (reg & AMS_IRQ_SHOCK)
ams_i2c_write(AMS_SHOCK, 0);
}
static u8 ams_i2c_get_vendor(void)
{
return ams_i2c_read(AMS_VENDOR);
}
static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z)
{
*x = ams_i2c_read(AMS_DATAX);
*y = ams_i2c_read(AMS_DATAY);
*z = ams_i2c_read(AMS_DATAZ);
}
static int ams_i2c_attach(struct i2c_adapter *adapter)
{
unsigned long bus;
int vmaj, vmin;
int result;
/* There can be only one */
if (unlikely(ams_info.has_device))
return -ENODEV;
if (strncmp(adapter->name, "uni-n", 5))
return -ENODEV;
bus = simple_strtoul(adapter->name + 6, NULL, 10);
if (bus != ams_info.i2c_bus)
return -ENODEV;
ams_info.i2c_client.addr = ams_info.i2c_address;
ams_info.i2c_client.adapter = adapter;
ams_info.i2c_client.driver = &ams_i2c_driver;
strcpy(ams_info.i2c_client.name, "Apple Motion Sensor");
if (ams_i2c_cmd(AMS_CMD_RESET)) {
printk(KERN_INFO "ams: Failed to reset the device\n");
return -ENODEV;
}
if (ams_i2c_cmd(AMS_CMD_START)) {
printk(KERN_INFO "ams: Failed to start the device\n");
return -ENODEV;
}
/* get version/vendor information */
ams_i2c_write(AMS_CTRL1, 0x02);
ams_i2c_write(AMS_CTRL2, 0x85);
ams_i2c_write(AMS_CTRL3, 0x01);
ams_i2c_cmd(AMS_CMD_READMEM);
vmaj = ams_i2c_read(AMS_DATA1);
vmin = ams_i2c_read(AMS_DATA2);
if (vmaj != 1 || vmin != 52) {
printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n",
vmaj, vmin);
return -ENODEV;
}
ams_i2c_cmd(AMS_CMD_VERSION);
vmaj = ams_i2c_read(AMS_DATA1);
vmin = ams_i2c_read(AMS_DATA2);
if (vmaj != 0 || vmin != 1) {
printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n",
vmaj, vmin);
return -ENODEV;
}
/* Disable interrupts */
ams_i2c_set_irq(AMS_IRQ_ALL, 0);
result = ams_sensor_attach();
if (result < 0)
return result;
/* Set default values */
ams_i2c_write(AMS_SENSLOW, 0x15);
ams_i2c_write(AMS_SENSHIGH, 0x60);
ams_i2c_write(AMS_CTRLX, 0x08);
ams_i2c_write(AMS_CTRLY, 0x0F);
ams_i2c_write(AMS_CTRLZ, 0x4F);
ams_i2c_write(AMS_UNKNOWN1, 0x14);
/* Clear interrupts */
ams_i2c_clear_irq(AMS_IRQ_ALL);
ams_info.has_device = 1;
/* Enable interrupts */
ams_i2c_set_irq(AMS_IRQ_ALL, 1);
printk(KERN_INFO "ams: Found I2C based motion sensor\n");
return 0;
}
static int ams_i2c_detach(struct i2c_adapter *adapter)
{
if (ams_info.has_device) {
/* Disable interrupts */
ams_i2c_set_irq(AMS_IRQ_ALL, 0);
/* Clear interrupts */
ams_i2c_clear_irq(AMS_IRQ_ALL);
printk(KERN_INFO "ams: Unloading\n");
ams_info.has_device = 0;
}
return 0;
}
static void ams_i2c_exit(void)
{
i2c_del_driver(&ams_i2c_driver);
}
int __init ams_i2c_init(struct device_node *np)
{
char *tmp_bus;
int result;
u32 *prop;
mutex_lock(&ams_info.lock);
/* Set implementation stuff */
ams_info.of_node = np;
ams_info.exit = ams_i2c_exit;
ams_info.get_vendor = ams_i2c_get_vendor;
ams_info.get_xyz = ams_i2c_get_xyz;
ams_info.clear_irq = ams_i2c_clear_irq;
ams_info.bustype = BUS_I2C;
/* look for bus either using "reg" or by path */
prop = (u32*)get_property(ams_info.of_node, "reg", NULL);
if (!prop) {
result = -ENODEV;
goto exit;
}
tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@");
if (tmp_bus)
ams_info.i2c_bus = *(tmp_bus + 9) - '0';
else
ams_info.i2c_bus = ((*prop) >> 8) & 0x0f;
ams_info.i2c_address = ((*prop) & 0xff) >> 1;
result = i2c_add_driver(&ams_i2c_driver);
exit:
mutex_unlock(&ams_info.lock);
return result;
}
/*
* Apple Motion Sensor driver (joystick emulation)
*
* Copyright (C) 2005 Stelian Pop (stelian@popies.net)
* Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
*
* 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.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include "ams.h"
static unsigned int joystick;
module_param(joystick, bool, 0644);
MODULE_PARM_DESC(joystick, "Enable the input class device on module load");
static unsigned int invert;
module_param(invert, bool, 0644);
MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
static int ams_input_kthread(void *data)
{
s8 x, y, z;
while (!kthread_should_stop()) {
mutex_lock(&ams_info.lock);
ams_sensors(&x, &y, &z);
x -= ams_info.xcalib;
y -= ams_info.ycalib;
z -= ams_info.zcalib;
input_report_abs(ams_info.idev, ABS_X, invert ? -x : x);
input_report_abs(ams_info.idev, ABS_Y, invert ? -y : y);
input_report_abs(ams_info.idev, ABS_Z, z);
input_sync(ams_info.idev);
mutex_unlock(&ams_info.lock);
msleep(25);
}
return 0;
}
static int ams_input_open(struct input_dev *dev)
{
ams_info.kthread = kthread_run(ams_input_kthread, NULL, "kams");
return IS_ERR(ams_info.kthread) ? PTR_ERR(ams_info.kthread) : 0;
}
static void ams_input_close(struct input_dev *dev)
{
kthread_stop(ams_info.kthread);
}
/* Call with ams_info.lock held! */
static void ams_input_enable(void)
{
s8 x, y, z;
if (ams_info.idev)
return;
ams_sensors(&x, &y, &z);
ams_info.xcalib = x;
ams_info.ycalib = y;
ams_info.zcalib = z;
ams_info.idev = input_allocate_device();
if (!ams_info.idev)
return;
ams_info.idev->name = "Apple Motion Sensor";
ams_info.idev->id.bustype = ams_info.bustype;
ams_info.idev->id.vendor = 0;
ams_info.idev->open = ams_input_open;
ams_info.idev->close = ams_input_close;
ams_info.idev->cdev.dev = &ams_info.of_dev->dev;
input_set_abs_params(ams_info.idev, ABS_X, -50, 50, 3, 0);
input_set_abs_params(ams_info.idev, ABS_Y, -50, 50, 3, 0);
input_set_abs_params(ams_info.idev, ABS_Z, -50, 50, 3, 0);
set_bit(EV_ABS, ams_info.idev->evbit);
set_bit(EV_KEY, ams_info.idev->evbit);
set_bit(BTN_TOUCH, ams_info.idev->keybit);
if (input_register_device(ams_info.idev)) {
input_free_device(ams_info.idev);
ams_info.idev = NULL;
return;
}
}
/* Call with ams_info.lock held! */
static void ams_input_disable(void)
{
if (ams_info.idev) {
input_unregister_device(ams_info.idev);
ams_info.idev = NULL;
}
}
static ssize_t ams_input_show_joystick(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", joystick);
}
static ssize_t ams_input_store_joystick(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
if (sscanf(buf, "%d\n", &joystick) != 1)
return -EINVAL;
mutex_lock(&ams_info.lock);
if (joystick)
ams_input_enable();
else
ams_input_disable();
mutex_unlock(&ams_info.lock);
return count;
}
static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR,
ams_input_show_joystick, ams_input_store_joystick);
/* Call with ams_info.lock held! */
int ams_input_init(void)
{
int result;
result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick);
if (!result && joystick)
ams_input_enable();
return result;
}
/* Call with ams_info.lock held! */
void ams_input_exit()
{
ams_input_disable();
device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick);
}
/*
* Apple Motion Sensor driver (PMU variant)
*
* Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
*
* 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.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include "ams.h"
/* Attitude */
#define AMS_X 0x00
#define AMS_Y 0x01
#define AMS_Z 0x02
/* Not exactly known, maybe chip vendor */
#define AMS_VENDOR 0x03
/* Freefall registers */
#define AMS_FF_CLEAR 0x04
#define AMS_FF_ENABLE 0x05
#define AMS_FF_LOW_LIMIT 0x06
#define AMS_FF_DEBOUNCE 0x07
/* Shock registers */
#define AMS_SHOCK_CLEAR 0x08
#define AMS_SHOCK_ENABLE 0x09
#define AMS_SHOCK_HIGH_LIMIT 0x0a
#define AMS_SHOCK_DEBOUNCE 0x0b
/* Global interrupt and power control register */
#define AMS_CONTROL 0x0c
static u8 ams_pmu_cmd;
static void ams_pmu_req_complete(struct adb_request *req)
{
complete((struct completion *)req->arg);
}
/* Only call this function from task context */
static void ams_pmu_set_register(u8 reg, u8 value)
{
static struct adb_request req;
DECLARE_COMPLETION(req_complete);
req.arg = &req_complete;
if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value))
return;
wait_for_completion(&req_complete);
}
/* Only call this function from task context */
static u8 ams_pmu_get_register(u8 reg)
{
static struct adb_request req;
DECLARE_COMPLETION(req_complete);
req.arg = &req_complete;
if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg))
return 0;
wait_for_completion(&req_complete);
if (req.reply_len > 0)
return req.reply[0];
else
return 0;
}
/* Enables or disables the specified interrupts */
static void ams_pmu_set_irq(enum ams_irq reg, char enable)
{
if (reg & AMS_IRQ_FREEFALL) {
u8 val = ams_pmu_get_register(AMS_FF_ENABLE);
if (enable)
val |= 0x80;
else
val &= ~0x80;
ams_pmu_set_register(AMS_FF_ENABLE, val);
}
if (reg & AMS_IRQ_SHOCK) {
u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE);
if (enable)
val |= 0x80;
else
val &= ~0x80;
ams_pmu_set_register(AMS_SHOCK_ENABLE, val);
}
if (reg & AMS_IRQ_GLOBAL) {
u8 val = ams_pmu_get_register(AMS_CONTROL);
if (enable)
val |= 0x80;
else
val &= ~0x80;
ams_pmu_set_register(AMS_CONTROL, val);
}
}
static void ams_pmu_clear_irq(enum ams_irq reg)
{
if (reg & AMS_IRQ_FREEFALL)
ams_pmu_set_register(AMS_FF_CLEAR, 0x00);
if (reg & AMS_IRQ_SHOCK)
ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00);
}
static u8 ams_pmu_get_vendor(void)
{
return ams_pmu_get_register(AMS_VENDOR);
}
static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
{
*x = ams_pmu_get_register(AMS_X);
*y = ams_pmu_get_register(AMS_Y);
*z = ams_pmu_get_register(AMS_Z);
}
static void ams_pmu_exit(void)
{
/* Disable interrupts */
ams_pmu_set_irq(AMS_IRQ_ALL, 0);
/* Clear interrupts */
ams_pmu_clear_irq(AMS_IRQ_ALL);
ams_info.has_device = 0;
printk(KERN_INFO "ams: Unloading\n");
}
int __init ams_pmu_init(struct device_node *np)
{
u32 *prop;
int result;
mutex_lock(&ams_info.lock);
/* Set implementation stuff */
ams_info.of_node = np;
ams_info.exit = ams_pmu_exit;
ams_info.get_vendor = ams_pmu_get_vendor;
ams_info.get_xyz = ams_pmu_get_xyz;
ams_info.clear_irq = ams_pmu_clear_irq;
ams_info.bustype = BUS_HOST;
/* Get PMU command, should be 0x4e, but we can never know */
prop = (u32*)get_property(ams_info.of_node, "reg", NULL);
if (!prop) {
result = -ENODEV;
goto exit;
}
ams_pmu_cmd = ((*prop) >> 8) & 0xff;
/* Disable interrupts */
ams_pmu_set_irq(AMS_IRQ_ALL, 0);
/* Clear interrupts */
ams_pmu_clear_irq(AMS_IRQ_ALL);
result = ams_sensor_attach();
if (result < 0)
goto exit;
/* Set default values */
ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15);
ams_pmu_set_register(AMS_FF_ENABLE, 0x08);
ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14);
ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60);
ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f);
ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14);
ams_pmu_set_register(AMS_CONTROL, 0x4f);
/* Clear interrupts */
ams_pmu_clear_irq(AMS_IRQ_ALL);
ams_info.has_device = 1;
/* Enable interrupts */
ams_pmu_set_irq(AMS_IRQ_ALL, 1);
printk(KERN_INFO "ams: Found PMU based motion sensor\n");
result = 0;
exit:
mutex_unlock(&ams_info.lock);
return result;
}
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/of_device.h>
enum ams_irq {
AMS_IRQ_FREEFALL = 0x01,
AMS_IRQ_SHOCK = 0x02,
AMS_IRQ_GLOBAL = 0x04,
AMS_IRQ_ALL =
AMS_IRQ_FREEFALL |
AMS_IRQ_SHOCK |
AMS_IRQ_GLOBAL,
};
struct ams {
/* Locks */
spinlock_t irq_lock;
struct mutex lock;
/* General properties */
struct device_node *of_node;
struct of_device *of_dev;
char has_device;
char vflag;
u32 orient1;
u32 orient2;
/* Interrupt worker */
struct work_struct worker;
u8 worker_irqs;
/* Implementation
*
* Only call these functions with the main lock held.
*/
void (*exit)(void);
void (*get_xyz)(s8 *x, s8 *y, s8 *z);
u8 (*get_vendor)(void);
void (*clear_irq)(enum ams_irq reg);
#ifdef CONFIG_SENSORS_AMS_I2C
/* I2C properties */
int i2c_bus;
int i2c_address;
struct i2c_client i2c_client;
#endif
/* Joystick emulation */
struct task_struct *kthread;
struct input_dev *idev;
__u16 bustype;
/* calibrated null values */
int xcalib, ycalib, zcalib;
};
extern struct ams ams_info;
extern void ams_sensors(s8 *x, s8 *y, s8 *z);
extern int ams_sensor_attach(void);
extern int ams_pmu_init(struct device_node *np);
extern int ams_i2c_init(struct device_node *np);
extern int ams_input_init(void);
extern void ams_input_exit(void);
This diff is collapsed.
......@@ -478,74 +478,64 @@ static struct attribute_group hdaps_attribute_group = {
/* Module stuff */
/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */
static int hdaps_dmi_match(struct dmi_system_id *id)
static int __init hdaps_dmi_match(struct dmi_system_id *id)
{
printk(KERN_INFO "hdaps: %s detected.\n", id->ident);
return 1;
}
/* hdaps_dmi_match_invert - found an inverted match. */
static int hdaps_dmi_match_invert(struct dmi_system_id *id)
static int __init hdaps_dmi_match_invert(struct dmi_system_id *id)
{
hdaps_invert = 1;
printk(KERN_INFO "hdaps: inverting axis readings.\n");
return hdaps_dmi_match(id);
}
#define HDAPS_DMI_MATCH_NORMAL(model) { \
.ident = "IBM " model, \
#define HDAPS_DMI_MATCH_NORMAL(vendor, model) { \
.ident = vendor " " model, \
.callback = hdaps_dmi_match, \
.matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
} \
}
#define HDAPS_DMI_MATCH_INVERT(model) { \
.ident = "IBM " model, \
#define HDAPS_DMI_MATCH_INVERT(vendor, model) { \
.ident = vendor " " model, \
.callback = hdaps_dmi_match_invert, \
.matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
} \
}
#define HDAPS_DMI_MATCH_LENOVO(model) { \
.ident = "Lenovo " model, \
.callback = hdaps_dmi_match_invert, \
.matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), \
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
} \
}
/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
"ThinkPad T42p", so the order of the entries matters.
If your ThinkPad is not recognized, please update to latest
BIOS. This is especially the case for some R52 ThinkPads. */
static struct dmi_system_id __initdata hdaps_whitelist[] = {
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
{ .ident = NULL }
};
static int __init hdaps_init(void)
{
int ret;
/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
"ThinkPad T42p", so the order of the entries matters */
struct dmi_system_id hdaps_whitelist[] = {
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"),
HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */
HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"),
HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad T60"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"),
{ .ident = NULL }
};
if (!dmi_check_system(hdaps_whitelist)) {
printk(KERN_WARNING "hdaps: supported laptop not found!\n");
ret = -ENODEV;
......
/*
hwmon-vid.c - VID/VRM/VRD voltage conversions
Copyright (c) 2004 Rudolf Marek <r.marek@sh.cvut.cz>
Copyright (c) 2004 Rudolf Marek <r.marek@assembler.cz>
Partly imported from i2c-vid.h of the lm_sensors project
Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
......@@ -232,7 +232,7 @@ u8 vid_which_vrm(void)
EXPORT_SYMBOL(vid_from_reg);
EXPORT_SYMBOL(vid_which_vrm);
MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>");
MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
MODULE_DESCRIPTION("hwmon-vid driver");
MODULE_LICENSE("GPL");
......@@ -3,7 +3,7 @@
monitoring.
Supports: IT8705F Super I/O chip w/LPC interface
IT8712F Super I/O chip w/LPC interface & SMBus
IT8712F Super I/O chip w/LPC interface
IT8716F Super I/O chip w/LPC interface
IT8718F Super I/O chip w/LPC interface
Sis950 A clone of the IT8705F
......@@ -41,12 +41,8 @@
#include <asm/io.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
static unsigned short isa_address;
/* Insmod parameters */
I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718);
enum chips { it87, it8712, it8716, it8718 };
#define REG 0x2e /* The register to read/write */
#define DEV 0x07 /* Register: Logical device select */
......@@ -162,8 +158,6 @@ static u8 vid_value;
#define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2)
#define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2)
#define IT87_REG_I2C_ADDR 0x48
#define IT87_REG_VIN_ENABLE 0x50
#define IT87_REG_TEMP_ENABLE 0x51
......@@ -242,33 +236,22 @@ struct it87_data {
};
static int it87_attach_adapter(struct i2c_adapter *adapter);
static int it87_isa_attach_adapter(struct i2c_adapter *adapter);
static int it87_detect(struct i2c_adapter *adapter, int address, int kind);
static int it87_detect(struct i2c_adapter *adapter);
static int it87_detach_client(struct i2c_client *client);
static int it87_read_value(struct i2c_client *client, u8 reg);
static int it87_write_value(struct i2c_client *client, u8 reg, u8 value);
static void it87_write_value(struct i2c_client *client, u8 reg, u8 value);
static struct it87_data *it87_update_device(struct device *dev);
static int it87_check_pwm(struct i2c_client *client);
static void it87_init_client(struct i2c_client *client, struct it87_data *data);
static struct i2c_driver it87_driver = {
.driver = {
.name = "it87",
},
.id = I2C_DRIVERID_IT87,
.attach_adapter = it87_attach_adapter,
.detach_client = it87_detach_client,
};
static struct i2c_driver it87_isa_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "it87-isa",
},
.attach_adapter = it87_isa_attach_adapter,
.attach_adapter = it87_detect,
.detach_client = it87_detach_client,
};
......@@ -850,22 +833,6 @@ static const struct attribute_group it87_group_opt = {
.attrs = it87_attributes_opt,
};
/* This function is called when:
* it87_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and it87_driver is still present) */
static int it87_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, it87_detect);
}
static int it87_isa_attach_adapter(struct i2c_adapter *adapter)
{
return it87_detect(adapter, isa_address, -1);
}
/* SuperIO detection - will change isa_address if a chip is found */
static int __init it87_find(unsigned short *address)
{
......@@ -916,29 +883,20 @@ static int __init it87_find(unsigned short *address)
}
/* This function is called by i2c_probe */
static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
static int it87_detect(struct i2c_adapter *adapter)
{
int i;
struct i2c_client *new_client;
struct it87_data *data;
int err = 0;
const char *name = "";
int is_isa = i2c_is_isa_adapter(adapter);
const char *name;
int enable_pwm_interface;
if (!is_isa &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto ERROR0;
/* Reserve the ISA region */
if (is_isa)
if (!request_region(address, IT87_EXTENT,
it87_isa_driver.driver.name))
goto ERROR0;
/* For now, we presume we have a valid client. We create the
client structure, even though we cannot fill it completely yet.
But it allows us to access it87_{read,write}_value. */
if (!request_region(isa_address, IT87_EXTENT,
it87_isa_driver.driver.name)){
err = -EBUSY;
goto ERROR0;
}
if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) {
err = -ENOMEM;
......@@ -946,80 +904,46 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
}
new_client = &data->client;
if (is_isa)
mutex_init(&data->lock);
mutex_init(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->addr = isa_address;
new_client->adapter = adapter;
new_client->driver = is_isa ? &it87_isa_driver : &it87_driver;
new_client->flags = 0;
new_client->driver = &it87_isa_driver;
/* Now, we do the remaining detection. */
if (kind < 0) {
if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80)
|| (!is_isa
&& it87_read_value(new_client, IT87_REG_I2C_ADDR) != address)) {
err = -ENODEV;
goto ERROR2;
}
if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80)
|| it87_read_value(new_client, IT87_REG_CHIPID) != 0x90) {
err = -ENODEV;
goto ERROR2;
}
/* Determine the chip type. */
if (kind <= 0) {
i = it87_read_value(new_client, IT87_REG_CHIPID);
if (i == 0x90) {
kind = it87;
if (is_isa) {
switch (chip_type) {
case IT8712F_DEVID:
kind = it8712;
break;
case IT8716F_DEVID:
kind = it8716;
break;
case IT8718F_DEVID:
kind = it8718;
break;
}
}
}
else {
if (kind == 0)
dev_info(&adapter->dev,
"Ignoring 'force' parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
err = -ENODEV;
goto ERROR2;
}
}
if (kind == it87) {
name = "it87";
} else if (kind == it8712) {
switch (chip_type) {
case IT8712F_DEVID:
data->type = it8712;
name = "it8712";
} else if (kind == it8716) {
break;
case IT8716F_DEVID:
data->type = it8716;
name = "it8716";
} else if (kind == it8718) {
break;
case IT8718F_DEVID:
data->type = it8718;
name = "it8718";
break;
default:
data->type = it87;
name = "it87";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
data->type = kind;
data->valid = 0;
mutex_init(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR2;
if (!is_isa)
dev_info(&new_client->dev, "The I2C interface to IT87xxF "
"hardware monitoring chips is deprecated. Please "
"report if you still rely on it.\n");
/* Check PWM configuration */
enable_pwm_interface = it87_check_pwm(new_client);
......@@ -1129,8 +1053,7 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
ERROR2:
kfree(data);
ERROR1:
if (is_isa)
release_region(address, IT87_EXTENT);
release_region(isa_address, IT87_EXTENT);
ERROR0:
return err;
}
......@@ -1147,50 +1070,39 @@ static int it87_detach_client(struct i2c_client *client)
if ((err = i2c_detach_client(client)))
return err;
if(i2c_is_isa_client(client))
release_region(client->addr, IT87_EXTENT);
release_region(client->addr, IT87_EXTENT);
kfree(data);
return 0;
}
/* The SMBus locks itself, but ISA access must be locked explicitly!
We don't want to lock the whole ISA bus, so we lock each client
separately.
/* ISA access must be locked explicitly!
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary. */
static int it87_read_value(struct i2c_client *client, u8 reg)
{
struct it87_data *data = i2c_get_clientdata(client);
int res;
if (i2c_is_isa_client(client)) {
mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
res = inb_p(client->addr + IT87_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return res;
} else
return i2c_smbus_read_byte_data(client, reg);
mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
res = inb_p(client->addr + IT87_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return res;
}
/* The SMBus locks itself, but ISA access muse be locked explicitly!
We don't want to lock the whole ISA bus, so we lock each client
separately.
/* ISA access must be locked explicitly!
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary. */
static int it87_write_value(struct i2c_client *client, u8 reg, u8 value)
static void it87_write_value(struct i2c_client *client, u8 reg, u8 value)
{
struct it87_data *data = i2c_get_clientdata(client);
if (i2c_is_isa_client(client)) {
mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
outb_p(value, client->addr + IT87_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return 0;
} else
return i2c_smbus_write_byte_data(client, reg, value);
mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
outb_p(value, client->addr + IT87_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
}
/* Return 1 if and only if the PWM interface is safe to use */
......@@ -1426,26 +1338,14 @@ static int __init sm_it87_init(void)
{
int res;
res = i2c_add_driver(&it87_driver);
if (res)
if ((res = it87_find(&isa_address)))
return res;
if (!it87_find(&isa_address)) {
res = i2c_isa_add_driver(&it87_isa_driver);
if (res) {
i2c_del_driver(&it87_driver);
return res;
}
}
return 0;
return i2c_isa_add_driver(&it87_isa_driver);
}
static void __exit sm_it87_exit(void)
{
if (isa_address)
i2c_isa_del_driver(&it87_isa_driver);
i2c_del_driver(&it87_driver);
i2c_isa_del_driver(&it87_isa_driver);
}
......
/*
* k8temp.c - Linux kernel module for hardware monitoring
*
* Copyright (C) 2006 Rudolf Marek <r.marek@sh.cvut.cz>
* Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz>
*
* Inspired from the w83785 and amd756 drivers.
*
......@@ -286,7 +286,7 @@ static void __exit k8temp_exit(void)
pci_unregister_driver(&k8temp_driver);
}
MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>");
MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
MODULE_DESCRIPTION("AMD K8 core temperature monitor");
MODULE_LICENSE("GPL");
......
......@@ -1000,7 +1000,7 @@ static int pc87360_detect(struct i2c_adapter *adapter)
(i&0x02) ? "external" : "internal");
data->vid_conf = confreg[3];
data->vrm = 90;
data->vrm = vid_which_vrm();
}
/* Fan clock dividers may be needed before any data is read */
......
This diff is collapsed.
......@@ -3,7 +3,7 @@
the Winbond W83627EHF Super-I/O chip
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
Copyright (C) 2006 Yuan Mu (Winbond),
Rudolf Marek <r.marek@sh.cvut.cz>
Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
Shamelessly ripped from the w83627hf driver
......
......@@ -3,7 +3,7 @@
monitoring
Copyright (C) 2004, 2005 Winbond Electronics Corp.
Chunhao Huang <DZShen@Winbond.com.tw>,
Rudolf Marek <r.marek@sh.cvut.cz>
Rudolf Marek <r.marek@assembler.cz>
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
......
This diff is collapsed.
......@@ -2,7 +2,7 @@
* i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
*
* Copyright (C) 2004 Patrick Mochel
* 2005 Rudolf Marek <r.marek@sh.cvut.cz>
* 2005 Rudolf Marek <r.marek@assembler.cz>
*
* The 1563 southbridge is deceptively similar to the 1533, with a
* few notable exceptions. One of those happens to be the fact they
......
......@@ -142,7 +142,6 @@
#define I2C_DRIVERID_MTP008 1023
#define I2C_DRIVERID_DS1621 1024
#define I2C_DRIVERID_ADM1024 1025
#define I2C_DRIVERID_IT87 1026
#define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */
#define I2C_DRIVERID_FSCPOS 1028
#define I2C_DRIVERID_FSCSCY 1029
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment