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: (29 commits) hwmon: Fix various typos hwmon: Check for ACPI resource conflicts hwmon: (lm70) Add TI TMP121 support hwmon: (lm70) Code streamlining and cleanup hwmon: Deprecate the fscher and fscpos drivers hwmon: (fschmd) Add watchdog support hwmon: (fschmd) Cleanups for watchdog support hwmon: (i5k_amb) Load automatically on all 5000/5400 chipsets hwmon: (it87) Add support for the ITE IT8720F hwmon: Don't overuse I2C_CLIENT_MODULE_PARM hwmon: Add LTC4245 driver hwmon: (f71882fg) Fix fan_to/from_reg prototypes hwmon: (f71882fg) Printout fan modes hwmon: (f71882fg) Add documentation hwmon: (f71882fg) Fix auto_channels_temp temp numbering with f8000 hwmon: (f71882fg) Add missing pwm3 attr for f71862fg hwmon: (f71882fg) Add F8000 support hwmon: (f71882fg) Remove the fan_mode module option hwmon: (f71882fg) Separate max and crit alarm and beep hwmon: (f71882fg) Check for hwmon powerdown state ...
This commit is contained in:
commit
2f2408a88c
|
@ -318,6 +318,14 @@ Who: Jean Delvare <khali@linux-fr.org>
|
|||
|
||||
---------------------------
|
||||
|
||||
What: fscher and fscpos drivers
|
||||
When: June 2009
|
||||
Why: Deprecated by the new fschmd driver.
|
||||
Who: Hans de Goede <hdegoede@redhat.com>
|
||||
Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: SELinux "compat_net" functionality
|
||||
When: 2.6.30 at the earliest
|
||||
Why: In 2.6.18 the Secmark concept was introduced to replace the "compat_net"
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
Kernel driver f71882fg
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
* Fintek F71882FG and F71883FG
|
||||
Prefix: 'f71882fg'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Available from the Fintek website
|
||||
* Fintek F71862FG and F71863FG
|
||||
Prefix: 'f71862fg'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Available from the Fintek website
|
||||
* Fintek F8000
|
||||
Prefix: 'f8000'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Not public
|
||||
|
||||
Author: Hans de Goede <hdegoede@redhat.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
Fintek F718xxFG/F8000 Super I/O chips include complete hardware monitoring
|
||||
capabilities. They can monitor up to 9 voltages (3 for the F8000), 4 fans and
|
||||
3 temperature sensors.
|
||||
|
||||
These chips also have fan controlling features, using either DC or PWM, in
|
||||
three different modes (one manual, two automatic).
|
||||
|
||||
The driver assumes that no more than one chip is present, which seems
|
||||
reasonable.
|
||||
|
||||
|
||||
Monitoring
|
||||
----------
|
||||
|
||||
The Voltage, Fan and Temperature Monitoring uses the standard sysfs
|
||||
interface as documented in sysfs-interface, without any exceptions.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
There are 2 modes to specify the speed of the fan, PWM duty cycle (or DC
|
||||
voltage) mode, where 0-100% duty cycle (0-100% of 12V) is specified. And RPM
|
||||
mode where the actual RPM of the fan (as measured) is controlled and the speed
|
||||
gets specified as 0-100% of the fan#_full_speed file.
|
||||
|
||||
Since both modes work in a 0-100% (mapped to 0-255) scale, there isn't a
|
||||
whole lot of a difference when modifying fan control settings. The only
|
||||
important difference is that in RPM mode the 0-100% controls the fan speed
|
||||
between 0-100% of fan#_full_speed. It is assumed that if the BIOS programs
|
||||
RPM mode, it will also set fan#_full_speed properly, if it does not then
|
||||
fan control will not work properly, unless you set a sane fan#_full_speed
|
||||
value yourself.
|
||||
|
||||
Switching between these modes requires re-initializing a whole bunch of
|
||||
registers, so the mode which the BIOS has set is kept. The mode is
|
||||
printed when loading the driver.
|
||||
|
||||
Three different fan control modes are supported; the mode number is written
|
||||
to the pwm#_enable file. Note that not all modes are supported on all
|
||||
chips, and some modes may only be available in RPM / PWM mode on the F8000.
|
||||
Writing an unsupported mode will result in an invalid parameter error.
|
||||
|
||||
* 1: Manual mode
|
||||
You ask for a specific PWM duty cycle / DC voltage or a specific % of
|
||||
fan#_full_speed by writing to the pwm# file. This mode is only
|
||||
available on the F8000 if the fan channel is in RPM mode.
|
||||
|
||||
* 2: Normal auto mode
|
||||
You can define a number of temperature/fan speed trip points, which % the
|
||||
fan should run at at this temp and which temp a fan should follow using the
|
||||
standard sysfs interface. The number and type of trip points is chip
|
||||
depended, see which files are available in sysfs.
|
||||
Fan/PWM channel 3 of the F8000 is always in this mode!
|
||||
|
||||
* 3: Thermostat mode (Only available on the F8000 when in duty cycle mode)
|
||||
The fan speed is regulated to keep the temp the fan is mapped to between
|
||||
temp#_auto_point2_temp and temp#_auto_point3_temp.
|
||||
|
||||
Both of the automatic modes require that pwm1 corresponds to fan1, pwm2 to
|
||||
fan2 and pwm3 to fan3.
|
|
@ -26,6 +26,10 @@ Supported chips:
|
|||
Datasheet: Publicly available at the ITE website
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8718F_V0.2.zip
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8718F_V0%203_(for%20C%20version).zip
|
||||
* IT8720F
|
||||
Prefix: 'it8720'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Not yet publicly available.
|
||||
* SiS950 [clone of IT8705F]
|
||||
Prefix: 'it87'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
|
@ -71,7 +75,7 @@ Description
|
|||
-----------
|
||||
|
||||
This driver implements support for the IT8705F, IT8712F, IT8716F,
|
||||
IT8718F, IT8726F and SiS950 chips.
|
||||
IT8718F, IT8720F, IT8726F and SiS950 chips.
|
||||
|
||||
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
|
||||
joysticks and other miscellaneous stuff. For hardware monitoring, they
|
||||
|
@ -84,19 +88,19 @@ the IT8716F and late IT8712F have 6. They are shared with other functions
|
|||
though, so the functionality may not be available on a given system.
|
||||
The driver dumbly assume it is there.
|
||||
|
||||
The IT8718F also features VID inputs (up to 8 pins) but the value is
|
||||
stored in the Super-I/O configuration space. Due to technical limitations,
|
||||
The IT8718F and IT8720F also features VID inputs (up to 8 pins) but the value
|
||||
is stored in the Super-I/O configuration space. Due to technical limitations,
|
||||
this value can currently only be read once at initialization time, so
|
||||
the driver won't notice and report changes in the VID value. The two
|
||||
upper VID bits share their pins with voltage inputs (in5 and in6) so you
|
||||
can't have both on a given board.
|
||||
|
||||
The IT8716F, IT8718F and later IT8712F revisions have support for
|
||||
The IT8716F, IT8718F, IT8720F and later IT8712F revisions have support for
|
||||
2 additional fans. The additional fans are supported by the driver.
|
||||
|
||||
The IT8716F and IT8718F, and late IT8712F and IT8705F also have optional
|
||||
16-bit tachometer counters for fans 1 to 3. This is better (no more fan
|
||||
clock divider mess) but not compatible with the older chips and
|
||||
The IT8716F, IT8718F and IT8720F, and late IT8712F and IT8705F also have
|
||||
optional 16-bit tachometer counters for fans 1 to 3. This is better (no more
|
||||
fan clock divider mess) but not compatible with the older chips and
|
||||
revisions. The 16-bit tachometer mode is enabled by the driver when one
|
||||
of the above chips is detected.
|
||||
|
||||
|
@ -122,7 +126,7 @@ zero'; this is important for negative voltage measurements. All voltage
|
|||
inputs can measure voltages between 0 and 4.08 volts, with a resolution of
|
||||
0.016 volt. The battery voltage in8 does not have limit registers.
|
||||
|
||||
The VID lines (IT8712F/IT8716F/IT8718F) encode the core voltage value:
|
||||
The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value:
|
||||
the voltage level your processor should work with. This is hardcoded by
|
||||
the mainboard and/or processor itself. It is a value in volts.
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
Kernel driver lm70
|
||||
==================
|
||||
|
||||
Supported chip:
|
||||
Supported chips:
|
||||
* National Semiconductor LM70
|
||||
Datasheet: http://www.national.com/pf/LM/LM70.html
|
||||
* Texas Instruments TMP121/TMP123
|
||||
Information: http://focus.ti.com/docs/prod/folders/print/tmp121.html
|
||||
|
||||
Author:
|
||||
Kaiwan N Billimoria <kaiwan@designergraphix.com>
|
||||
|
@ -25,6 +27,14 @@ complement digital temperature (sent via the SIO line), is available in the
|
|||
driver for interpretation. This driver makes use of the kernel's in-core
|
||||
SPI support.
|
||||
|
||||
As a real (in-tree) example of this "SPI protocol driver" interfacing
|
||||
with a "SPI master controller driver", see drivers/spi/spi_lm70llp.c
|
||||
and its associated documentation.
|
||||
|
||||
The TMP121/TMP123 are very similar; main differences are 4 wire SPI inter-
|
||||
face (read only) and 13-bit temperature data (0.0625 degrees celsius reso-
|
||||
lution).
|
||||
|
||||
Thanks to
|
||||
---------
|
||||
Jean Delvare <khali@linux-fr.org> for mentoring the hwmon-side driver
|
||||
|
|
|
@ -164,7 +164,7 @@ configured individually according to the following options.
|
|||
temperature. (PWM value from 0 to 255)
|
||||
|
||||
* pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature
|
||||
the bahaviour of fans. Write 1 to let fans spinning at
|
||||
the behaviour of fans. Write 1 to let fans spinning at
|
||||
pwm#_auto_pwm_min or write 0 to let them off.
|
||||
|
||||
NOTE: It has been reported that there is a bug in the LM85 that causes the flag
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
Kernel driver ltc4245
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Linear Technology LTC4245
|
||||
Prefix: 'ltc4245'
|
||||
Addresses scanned: 0x20-0x3f
|
||||
Datasheet:
|
||||
http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517
|
||||
|
||||
Author: Ira W. Snyder <iws@ovro.caltech.edu>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The LTC4245 controller allows a board to be safely inserted and removed
|
||||
from a live backplane in multiple supply systems such as CompactPCI and
|
||||
PCI Express.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for LTC4245 devices, due to the fact that some
|
||||
of the possible addresses are unfriendly to probing. You will need to use
|
||||
the "force" parameter to tell the driver where to find the device.
|
||||
|
||||
Example: the following will load the driver for an LTC4245 at address 0x23
|
||||
on I2C bus #1:
|
||||
$ modprobe ltc4245 force=1,0x23
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The LTC4245 has built-in limits for over and under current warnings. This
|
||||
makes it very likely that the reference circuit will be used.
|
||||
|
||||
This driver uses the values in the datasheet to change the register values
|
||||
into the values specified in the sysfs-interface document. The current readings
|
||||
rely on the sense resistors listed in Table 2: "Sense Resistor Values".
|
||||
|
||||
in1_input 12v input voltage (mV)
|
||||
in2_input 5v input voltage (mV)
|
||||
in3_input 3v input voltage (mV)
|
||||
in4_input Vee (-12v) input voltage (mV)
|
||||
|
||||
in1_min_alarm 12v input undervoltage alarm
|
||||
in2_min_alarm 5v input undervoltage alarm
|
||||
in3_min_alarm 3v input undervoltage alarm
|
||||
in4_min_alarm Vee (-12v) input undervoltage alarm
|
||||
|
||||
curr1_input 12v current (mA)
|
||||
curr2_input 5v current (mA)
|
||||
curr3_input 3v current (mA)
|
||||
curr4_input Vee (-12v) current (mA)
|
||||
|
||||
curr1_max_alarm 12v overcurrent alarm
|
||||
curr2_max_alarm 5v overcurrent alarm
|
||||
curr3_max_alarm 3v overcurrent alarm
|
||||
curr4_max_alarm Vee (-12v) overcurrent alarm
|
||||
|
||||
in5_input 12v output voltage (mV)
|
||||
in6_input 5v output voltage (mV)
|
||||
in7_input 3v output voltage (mV)
|
||||
in8_input Vee (-12v) output voltage (mV)
|
||||
|
||||
in5_min_alarm 12v output undervoltage alarm
|
||||
in6_min_alarm 5v output undervoltage alarm
|
||||
in7_min_alarm 3v output undervoltage alarm
|
||||
in8_min_alarm Vee (-12v) output undervoltage alarm
|
||||
|
||||
in9_input GPIO #1 voltage data
|
||||
in10_input GPIO #2 voltage data
|
||||
in11_input GPIO #3 voltage data
|
||||
|
||||
power1_input 12v power usage (mW)
|
||||
power2_input 5v power usage (mW)
|
||||
power3_input 3v power usage (mW)
|
||||
power4_input Vee (-12v) power usage (mW)
|
|
@ -13,10 +13,20 @@ Description
|
|||
This driver provides glue code connecting a National Semiconductor LM70 LLP
|
||||
temperature sensor evaluation board to the kernel's SPI core subsystem.
|
||||
|
||||
This is a SPI master controller driver. It can be used in conjunction with
|
||||
(layered under) the LM70 logical driver (a "SPI protocol driver").
|
||||
In effect, this driver turns the parallel port interface on the eval board
|
||||
into a SPI bus with a single device, which will be driven by the generic
|
||||
LM70 driver (drivers/hwmon/lm70.c).
|
||||
|
||||
|
||||
Hardware Interfacing
|
||||
--------------------
|
||||
The schematic for this particular board (the LM70EVAL-LLP) is
|
||||
available (on page 4) here:
|
||||
|
||||
http://www.national.com/appinfo/tempsensors/files/LM70LLPEVALmanual.pdf
|
||||
|
||||
The hardware interfacing on the LM70 LLP eval board is as follows:
|
||||
|
||||
Parallel LM70 LLP
|
||||
|
|
|
@ -284,11 +284,12 @@ config SENSORS_F71805F
|
|||
will be called f71805f.
|
||||
|
||||
config SENSORS_F71882FG
|
||||
tristate "Fintek F71882FG and F71883FG"
|
||||
tristate "Fintek F71862FG, F71882FG and F8000"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for hardware monitoring
|
||||
features of the Fintek F71882FG and F71883FG Super-I/O chips.
|
||||
features of the Fintek F71882FG/F71883FG, F71862FG/71863FG
|
||||
and F8000 Super-I/O chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called f71882fg.
|
||||
|
@ -304,9 +305,13 @@ config SENSORS_F75375S
|
|||
will be called f75375s.
|
||||
|
||||
config SENSORS_FSCHER
|
||||
tristate "FSC Hermes"
|
||||
tristate "FSC Hermes (DEPRECATED)"
|
||||
depends on X86 && I2C
|
||||
help
|
||||
This driver is DEPRECATED please use the new merged fschmd
|
||||
("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver
|
||||
instead.
|
||||
|
||||
If you say yes here you get support for Fujitsu Siemens
|
||||
Computers Hermes sensor chips.
|
||||
|
||||
|
@ -314,9 +319,13 @@ config SENSORS_FSCHER
|
|||
will be called fscher.
|
||||
|
||||
config SENSORS_FSCPOS
|
||||
tristate "FSC Poseidon"
|
||||
tristate "FSC Poseidon (DEPRECATED)"
|
||||
depends on X86 && I2C
|
||||
help
|
||||
This driver is DEPRECATED please use the new merged fschmd
|
||||
("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver
|
||||
instead.
|
||||
|
||||
If you say yes here you get support for Fujitsu Siemens
|
||||
Computers Poseidon sensor chips.
|
||||
|
||||
|
@ -325,14 +334,15 @@ config SENSORS_FSCPOS
|
|||
|
||||
config SENSORS_FSCHMD
|
||||
tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles"
|
||||
depends on X86 && I2C && EXPERIMENTAL
|
||||
depends on X86 && I2C
|
||||
help
|
||||
If you say yes here you get support for various Fujitsu Siemens
|
||||
Computers sensor chips.
|
||||
Computers sensor chips, including support for the integrated
|
||||
watchdog.
|
||||
|
||||
This is a new merged driver for FSC sensor chips which is intended
|
||||
as a replacment for the fscpos, fscscy and fscher drivers and adds
|
||||
support for several other FCS sensor chips.
|
||||
This is a merged driver for FSC sensor chips replacing the fscpos,
|
||||
fscscy and fscher drivers and adding support for several other FSC
|
||||
sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called fschmd.
|
||||
|
@ -399,7 +409,8 @@ config SENSORS_IT87
|
|||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for ITE IT8705F, IT8712F,
|
||||
IT8716F, IT8718F and IT8726F sensor chips, and the SiS960 clone.
|
||||
IT8716F, IT8718F, IT8720F and IT8726F sensor chips, and the
|
||||
SiS960 clone.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called it87.
|
||||
|
@ -417,11 +428,12 @@ config SENSORS_LM63
|
|||
will be called lm63.
|
||||
|
||||
config SENSORS_LM70
|
||||
tristate "National Semiconductor LM70"
|
||||
tristate "National Semiconductor LM70 / Texas Instruments TMP121"
|
||||
depends on SPI_MASTER && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the National Semiconductor
|
||||
LM70 digital temperature sensor chip.
|
||||
LM70 and Texas Instruments TMP121/TMP123 digital temperature
|
||||
sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm70.
|
||||
|
@ -548,6 +560,17 @@ config SENSORS_LM93
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called lm93.
|
||||
|
||||
config SENSORS_LTC4245
|
||||
tristate "Linear Technology LTC4245"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for Linear Technology LTC4245
|
||||
Multiple Supply Hot Swap Controller I2C interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc4245.
|
||||
|
||||
config SENSORS_MAX1111
|
||||
tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o
|
|||
obj-$(CONFIG_SENSORS_LM90) += lm90.o
|
||||
obj-$(CONFIG_SENSORS_LM92) += lm92.o
|
||||
obj-$(CONFIG_SENSORS_LM93) += lm93.o
|
||||
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
|
||||
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
||||
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
||||
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||
|
|
|
@ -53,7 +53,10 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
|
|||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(asb100);
|
||||
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
|
||||
|
||||
static unsigned short force_subclients[4];
|
||||
module_param_array(force_subclients, short, NULL, 0);
|
||||
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
|
||||
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
|
||||
|
||||
/* Voltage IN registers 0-6 */
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* ISA device, if found */
|
||||
|
@ -2361,6 +2362,10 @@ static int __init dme1737_isa_device_add(unsigned short addr)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
if (!(pdev = platform_device_alloc("dme1737", addr))) {
|
||||
printk(KERN_ERR "dme1737: Failed to allocate device.\n");
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
|
@ -1455,6 +1456,10 @@ static int __init f71805f_device_add(unsigned short address,
|
|||
}
|
||||
|
||||
res.name = pdev->name;
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit_device_put;
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
/* fschmd.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans de Goede <j.w.r.degoede@hhs.nl>
|
||||
* Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -42,11 +42,20 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kref.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
|
||||
|
||||
/* Insmod parameters */
|
||||
static int nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, int, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
|
||||
|
||||
/*
|
||||
|
@ -63,13 +72,20 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
|
|||
#define FSCHMD_REG_EVENT_STATE 0x04
|
||||
#define FSCHMD_REG_CONTROL 0x05
|
||||
|
||||
#define FSCHMD_CONTROL_ALERT_LED_MASK 0x01
|
||||
#define FSCHMD_CONTROL_ALERT_LED 0x01
|
||||
|
||||
/* watchdog (support to be implemented) */
|
||||
/* watchdog */
|
||||
#define FSCHMD_REG_WDOG_PRESET 0x28
|
||||
#define FSCHMD_REG_WDOG_STATE 0x23
|
||||
#define FSCHMD_REG_WDOG_CONTROL 0x21
|
||||
|
||||
#define FSCHMD_WDOG_CONTROL_TRIGGER 0x10
|
||||
#define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */
|
||||
#define FSCHMD_WDOG_CONTROL_STOP 0x20
|
||||
#define FSCHMD_WDOG_CONTROL_RESOLUTION 0x40
|
||||
|
||||
#define FSCHMD_WDOG_STATE_CARDRESET 0x02
|
||||
|
||||
/* voltages, weird order is to keep the same order as the old drivers */
|
||||
static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 };
|
||||
|
||||
|
@ -115,8 +131,8 @@ static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = {
|
|||
static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 };
|
||||
|
||||
/* Fan status register bitmasks */
|
||||
#define FSCHMD_FAN_ALARM_MASK 0x04 /* called fault by FSC! */
|
||||
#define FSCHMD_FAN_NOT_PRESENT_MASK 0x08 /* not documented */
|
||||
#define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */
|
||||
#define FSCHMD_FAN_NOT_PRESENT 0x08 /* not documented */
|
||||
|
||||
|
||||
/* actual temperature registers */
|
||||
|
@ -158,14 +174,11 @@ static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */
|
|||
static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 };
|
||||
|
||||
/* temp status register bitmasks */
|
||||
#define FSCHMD_TEMP_WORKING_MASK 0x01
|
||||
#define FSCHMD_TEMP_ALERT_MASK 0x02
|
||||
#define FSCHMD_TEMP_WORKING 0x01
|
||||
#define FSCHMD_TEMP_ALERT 0x02
|
||||
/* there only really is an alarm if the sensor is working and alert == 1 */
|
||||
#define FSCHMD_TEMP_ALARM_MASK \
|
||||
(FSCHMD_TEMP_WORKING_MASK | FSCHMD_TEMP_ALERT_MASK)
|
||||
|
||||
/* our driver name */
|
||||
#define FSCHMD_NAME "fschmd"
|
||||
(FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT)
|
||||
|
||||
/*
|
||||
* Functions declarations
|
||||
|
@ -195,7 +208,7 @@ MODULE_DEVICE_TABLE(i2c, fschmd_id);
|
|||
static struct i2c_driver fschmd_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = FSCHMD_NAME,
|
||||
.name = "fschmd",
|
||||
},
|
||||
.probe = fschmd_probe,
|
||||
.remove = fschmd_remove,
|
||||
|
@ -209,14 +222,26 @@ static struct i2c_driver fschmd_driver = {
|
|||
*/
|
||||
|
||||
struct fschmd_data {
|
||||
struct i2c_client *client;
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
struct mutex watchdog_lock;
|
||||
struct list_head list; /* member of the watchdog_data_list */
|
||||
struct kref kref;
|
||||
struct miscdevice watchdog_miscdev;
|
||||
int kind;
|
||||
unsigned long watchdog_is_open;
|
||||
char watchdog_expect_close;
|
||||
char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* register values */
|
||||
u8 revision; /* chip revision */
|
||||
u8 global_control; /* global control register */
|
||||
u8 watchdog_control; /* watchdog control register */
|
||||
u8 watchdog_state; /* watchdog status register */
|
||||
u8 watchdog_preset; /* watchdog counter preset on trigger val */
|
||||
u8 volt[3]; /* 12, 5, battery voltage */
|
||||
u8 temp_act[5]; /* temperature */
|
||||
u8 temp_status[5]; /* status of sensor */
|
||||
|
@ -228,11 +253,28 @@ struct fschmd_data {
|
|||
};
|
||||
|
||||
/* Global variables to hold information read from special DMI tables, which are
|
||||
available on FSC machines with an fscher or later chip. */
|
||||
available on FSC machines with an fscher or later chip. There is no need to
|
||||
protect these with a lock as they are only modified from our attach function
|
||||
which always gets called with the i2c-core lock held and never accessed
|
||||
before the attach function is done with them. */
|
||||
static int dmi_mult[3] = { 490, 200, 100 };
|
||||
static int dmi_offset[3] = { 0, 0, 0 };
|
||||
static int dmi_vref = -1;
|
||||
|
||||
/* Somewhat ugly :( global data pointer list with all fschmd devices, so that
|
||||
we can find our device data as when using misc_register there is no other
|
||||
method to get to ones device data from the open fop. */
|
||||
static LIST_HEAD(watchdog_data_list);
|
||||
/* Note this lock not only protect list access, but also data.kref access */
|
||||
static DEFINE_MUTEX(watchdog_data_mutex);
|
||||
|
||||
/* Release our data struct when we're detached from the i2c client *and* all
|
||||
references to our watchdog device are released */
|
||||
static void fschmd_release_resources(struct kref *ref)
|
||||
{
|
||||
struct fschmd_data *data = container_of(ref, struct fschmd_data, kref);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs attr show / store functions
|
||||
|
@ -300,7 +342,7 @@ static ssize_t show_temp_fault(struct device *dev,
|
|||
struct fschmd_data *data = fschmd_update_device(dev);
|
||||
|
||||
/* bit 0 set means sensor working ok, so no fault! */
|
||||
if (data->temp_status[index] & FSCHMD_TEMP_WORKING_MASK)
|
||||
if (data->temp_status[index] & FSCHMD_TEMP_WORKING)
|
||||
return sprintf(buf, "0\n");
|
||||
else
|
||||
return sprintf(buf, "1\n");
|
||||
|
@ -385,7 +427,7 @@ static ssize_t show_fan_alarm(struct device *dev,
|
|||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct fschmd_data *data = fschmd_update_device(dev);
|
||||
|
||||
if (data->fan_status[index] & FSCHMD_FAN_ALARM_MASK)
|
||||
if (data->fan_status[index] & FSCHMD_FAN_ALARM)
|
||||
return sprintf(buf, "1\n");
|
||||
else
|
||||
return sprintf(buf, "0\n");
|
||||
|
@ -397,7 +439,7 @@ static ssize_t show_fan_fault(struct device *dev,
|
|||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct fschmd_data *data = fschmd_update_device(dev);
|
||||
|
||||
if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT_MASK)
|
||||
if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT)
|
||||
return sprintf(buf, "1\n");
|
||||
else
|
||||
return sprintf(buf, "0\n");
|
||||
|
@ -449,7 +491,7 @@ static ssize_t show_alert_led(struct device *dev,
|
|||
{
|
||||
struct fschmd_data *data = fschmd_update_device(dev);
|
||||
|
||||
if (data->global_control & FSCHMD_CONTROL_ALERT_LED_MASK)
|
||||
if (data->global_control & FSCHMD_CONTROL_ALERT_LED)
|
||||
return sprintf(buf, "1\n");
|
||||
else
|
||||
return sprintf(buf, "0\n");
|
||||
|
@ -467,9 +509,9 @@ static ssize_t store_alert_led(struct device *dev,
|
|||
reg = i2c_smbus_read_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL);
|
||||
|
||||
if (v)
|
||||
reg |= FSCHMD_CONTROL_ALERT_LED_MASK;
|
||||
reg |= FSCHMD_CONTROL_ALERT_LED;
|
||||
else
|
||||
reg &= ~FSCHMD_CONTROL_ALERT_LED_MASK;
|
||||
reg &= ~FSCHMD_CONTROL_ALERT_LED;
|
||||
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL, reg);
|
||||
|
||||
|
@ -551,7 +593,265 @@ static struct sensor_device_attribute fschmd_fan_attr[] = {
|
|||
|
||||
|
||||
/*
|
||||
* Real code
|
||||
* Watchdog routines
|
||||
*/
|
||||
|
||||
static int watchdog_set_timeout(struct fschmd_data *data, int timeout)
|
||||
{
|
||||
int ret, resolution;
|
||||
int kind = data->kind + 1; /* 0-x array index -> 1-x module param */
|
||||
|
||||
/* 2 second or 60 second resolution? */
|
||||
if (timeout <= 510 || kind == fscpos || kind == fscscy)
|
||||
resolution = 2;
|
||||
else
|
||||
resolution = 60;
|
||||
|
||||
if (timeout < resolution || timeout > (resolution * 255))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->watchdog_lock);
|
||||
if (!data->client) {
|
||||
ret = -ENODEV;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (resolution == 2)
|
||||
data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_RESOLUTION;
|
||||
else
|
||||
data->watchdog_control |= FSCHMD_WDOG_CONTROL_RESOLUTION;
|
||||
|
||||
data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
|
||||
|
||||
/* Write new timeout value */
|
||||
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_PRESET,
|
||||
data->watchdog_preset);
|
||||
/* Write new control register, do not trigger! */
|
||||
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL,
|
||||
data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER);
|
||||
|
||||
ret = data->watchdog_preset * resolution;
|
||||
|
||||
leave:
|
||||
mutex_unlock(&data->watchdog_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int watchdog_get_timeout(struct fschmd_data *data)
|
||||
{
|
||||
int timeout;
|
||||
|
||||
mutex_lock(&data->watchdog_lock);
|
||||
if (data->watchdog_control & FSCHMD_WDOG_CONTROL_RESOLUTION)
|
||||
timeout = data->watchdog_preset * 60;
|
||||
else
|
||||
timeout = data->watchdog_preset * 2;
|
||||
mutex_unlock(&data->watchdog_lock);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static int watchdog_trigger(struct fschmd_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&data->watchdog_lock);
|
||||
if (!data->client) {
|
||||
ret = -ENODEV;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER;
|
||||
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL,
|
||||
data->watchdog_control);
|
||||
leave:
|
||||
mutex_unlock(&data->watchdog_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int watchdog_stop(struct fschmd_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&data->watchdog_lock);
|
||||
if (!data->client) {
|
||||
ret = -ENODEV;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED;
|
||||
/* Don't store the stop flag in our watchdog control register copy, as
|
||||
its a write only bit (read always returns 0) */
|
||||
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL,
|
||||
data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP);
|
||||
leave:
|
||||
mutex_unlock(&data->watchdog_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int watchdog_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct fschmd_data *pos, *data = NULL;
|
||||
|
||||
/* We get called from drivers/char/misc.c with misc_mtx hold, and we
|
||||
call misc_register() from fschmd_probe() with watchdog_data_mutex
|
||||
hold, as misc_register() takes the misc_mtx lock, this is a possible
|
||||
deadlock, so we use mutex_trylock here. */
|
||||
if (!mutex_trylock(&watchdog_data_mutex))
|
||||
return -ERESTARTSYS;
|
||||
list_for_each_entry(pos, &watchdog_data_list, list) {
|
||||
if (pos->watchdog_miscdev.minor == iminor(inode)) {
|
||||
data = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Note we can never not have found data, so we don't check for this */
|
||||
kref_get(&data->kref);
|
||||
mutex_unlock(&watchdog_data_mutex);
|
||||
|
||||
if (test_and_set_bit(0, &data->watchdog_is_open))
|
||||
return -EBUSY;
|
||||
|
||||
/* Start the watchdog */
|
||||
watchdog_trigger(data);
|
||||
filp->private_data = data;
|
||||
|
||||
return nonseekable_open(inode, filp);
|
||||
}
|
||||
|
||||
static int watchdog_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct fschmd_data *data = filp->private_data;
|
||||
|
||||
if (data->watchdog_expect_close) {
|
||||
watchdog_stop(data);
|
||||
data->watchdog_expect_close = 0;
|
||||
} else {
|
||||
watchdog_trigger(data);
|
||||
dev_crit(&data->client->dev,
|
||||
"unexpected close, not stopping watchdog!\n");
|
||||
}
|
||||
|
||||
clear_bit(0, &data->watchdog_is_open);
|
||||
|
||||
mutex_lock(&watchdog_data_mutex);
|
||||
kref_put(&data->kref, fschmd_release_resources);
|
||||
mutex_unlock(&watchdog_data_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t watchdog_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
size_t ret;
|
||||
struct fschmd_data *data = filp->private_data;
|
||||
|
||||
if (count) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
/* Clear it in case it was set with a previous write */
|
||||
data->watchdog_expect_close = 0;
|
||||
|
||||
for (i = 0; i != count; i++) {
|
||||
char c;
|
||||
if (get_user(c, buf + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
data->watchdog_expect_close = 1;
|
||||
}
|
||||
}
|
||||
ret = watchdog_trigger(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int watchdog_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
static struct watchdog_info ident = {
|
||||
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
|
||||
WDIOF_CARDRESET,
|
||||
.identity = "FSC watchdog"
|
||||
};
|
||||
int i, ret = 0;
|
||||
struct fschmd_data *data = filp->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
ident.firmware_version = data->revision;
|
||||
if (!nowayout)
|
||||
ident.options |= WDIOF_MAGICCLOSE;
|
||||
if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
ret = put_user(0, (int __user *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
if (data->watchdog_state & FSCHMD_WDOG_STATE_CARDRESET)
|
||||
ret = put_user(WDIOF_CARDRESET, (int __user *)arg);
|
||||
else
|
||||
ret = put_user(0, (int __user *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
ret = watchdog_trigger(data);
|
||||
break;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
i = watchdog_get_timeout(data);
|
||||
ret = put_user(i, (int __user *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(i, (int __user *)arg)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = watchdog_set_timeout(data, i);
|
||||
if (ret > 0)
|
||||
ret = put_user(ret, (int __user *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(i, (int __user *)arg)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i & WDIOS_DISABLECARD)
|
||||
ret = watchdog_stop(data);
|
||||
else if (i & WDIOS_ENABLECARD)
|
||||
ret = watchdog_trigger(data);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations watchdog_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = watchdog_open,
|
||||
.release = watchdog_release,
|
||||
.write = watchdog_write,
|
||||
.ioctl = watchdog_ioctl,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Detect, register, unregister and update device functions
|
||||
*/
|
||||
|
||||
/* DMI decode routine to read voltage scaling factors from special DMI tables,
|
||||
|
@ -661,9 +961,9 @@ static int fschmd_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct fschmd_data *data;
|
||||
u8 revision;
|
||||
const char * const names[5] = { "Poseidon", "Hermes", "Scylla",
|
||||
"Heracles", "Heimdall" };
|
||||
const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
|
||||
int i, err;
|
||||
enum chips kind = id->driver_data;
|
||||
|
||||
|
@ -673,6 +973,13 @@ static int fschmd_probe(struct i2c_client *client,
|
|||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
mutex_init(&data->watchdog_lock);
|
||||
INIT_LIST_HEAD(&data->list);
|
||||
kref_init(&data->kref);
|
||||
/* Store client pointer in our data struct for watchdog usage
|
||||
(where the client is found through a data ptr instead of the
|
||||
otherway around) */
|
||||
data->client = client;
|
||||
|
||||
if (kind == fscpos) {
|
||||
/* The Poseidon has hardwired temp limits, fill these
|
||||
|
@ -683,16 +990,27 @@ static int fschmd_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
/* Read the special DMI table for fscher and newer chips */
|
||||
if (kind == fscher || kind >= fschrc) {
|
||||
if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) {
|
||||
dmi_walk(fschmd_dmi_decode);
|
||||
if (dmi_vref == -1) {
|
||||
printk(KERN_WARNING FSCHMD_NAME
|
||||
": Couldn't get voltage scaling factors from "
|
||||
dev_warn(&client->dev,
|
||||
"Couldn't get voltage scaling factors from "
|
||||
"BIOS DMI table, using builtin defaults\n");
|
||||
dmi_vref = 33;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read in some never changing registers */
|
||||
data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
|
||||
data->global_control = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_CONTROL);
|
||||
data->watchdog_control = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_WDOG_CONTROL);
|
||||
data->watchdog_state = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_WDOG_STATE);
|
||||
data->watchdog_preset = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_WDOG_PRESET);
|
||||
|
||||
/* i2c kind goes from 1-5, we want from 0-4 to address arrays */
|
||||
data->kind = kind - 1;
|
||||
|
||||
|
@ -735,9 +1053,43 @@ static int fschmd_probe(struct i2c_client *client,
|
|||
goto exit_detach;
|
||||
}
|
||||
|
||||
revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
|
||||
printk(KERN_INFO FSCHMD_NAME ": Detected FSC %s chip, revision: %d\n",
|
||||
names[data->kind], (int) revision);
|
||||
/* We take the data_mutex lock early so that watchdog_open() cannot
|
||||
run when misc_register() has completed, but we've not yet added
|
||||
our data to the watchdog_data_list (and set the default timeout) */
|
||||
mutex_lock(&watchdog_data_mutex);
|
||||
for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
|
||||
/* Register our watchdog part */
|
||||
snprintf(data->watchdog_name, sizeof(data->watchdog_name),
|
||||
"watchdog%c", (i == 0) ? '\0' : ('0' + i));
|
||||
data->watchdog_miscdev.name = data->watchdog_name;
|
||||
data->watchdog_miscdev.fops = &watchdog_fops;
|
||||
data->watchdog_miscdev.minor = watchdog_minors[i];
|
||||
err = misc_register(&data->watchdog_miscdev);
|
||||
if (err == -EBUSY)
|
||||
continue;
|
||||
if (err) {
|
||||
data->watchdog_miscdev.minor = 0;
|
||||
dev_err(&client->dev,
|
||||
"Registering watchdog chardev: %d\n", err);
|
||||
break;
|
||||
}
|
||||
|
||||
list_add(&data->list, &watchdog_data_list);
|
||||
watchdog_set_timeout(data, 60);
|
||||
dev_info(&client->dev,
|
||||
"Registered watchdog chardev major 10, minor: %d\n",
|
||||
watchdog_minors[i]);
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(watchdog_minors)) {
|
||||
data->watchdog_miscdev.minor = 0;
|
||||
dev_warn(&client->dev, "Couldn't register watchdog chardev "
|
||||
"(due to no free minor)\n");
|
||||
}
|
||||
mutex_unlock(&watchdog_data_mutex);
|
||||
|
||||
dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n",
|
||||
names[data->kind], (int) data->revision);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -751,6 +1103,24 @@ static int fschmd_remove(struct i2c_client *client)
|
|||
struct fschmd_data *data = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
/* Unregister the watchdog (if registered) */
|
||||
if (data->watchdog_miscdev.minor) {
|
||||
misc_deregister(&data->watchdog_miscdev);
|
||||
if (data->watchdog_is_open) {
|
||||
dev_warn(&client->dev,
|
||||
"i2c client detached with watchdog open! "
|
||||
"Stopping watchdog.\n");
|
||||
watchdog_stop(data);
|
||||
}
|
||||
mutex_lock(&watchdog_data_mutex);
|
||||
list_del(&data->list);
|
||||
mutex_unlock(&watchdog_data_mutex);
|
||||
/* Tell the watchdog code the client is gone */
|
||||
mutex_lock(&data->watchdog_lock);
|
||||
data->client = NULL;
|
||||
mutex_unlock(&data->watchdog_lock);
|
||||
}
|
||||
|
||||
/* Check if registered in case we're called from fschmd_detect
|
||||
to cleanup after an error */
|
||||
if (data->hwmon_dev)
|
||||
|
@ -765,7 +1135,10 @@ static int fschmd_remove(struct i2c_client *client)
|
|||
device_remove_file(&client->dev,
|
||||
&fschmd_fan_attr[i].dev_attr);
|
||||
|
||||
kfree(data);
|
||||
mutex_lock(&watchdog_data_mutex);
|
||||
kref_put(&data->kref, fschmd_release_resources);
|
||||
mutex_unlock(&watchdog_data_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -798,7 +1171,7 @@ static struct fschmd_data *fschmd_update_device(struct device *dev)
|
|||
data->temp_act[i] < data->temp_max[i])
|
||||
i2c_smbus_write_byte_data(client,
|
||||
FSCHMD_REG_TEMP_STATE[data->kind][i],
|
||||
FSCHMD_TEMP_ALERT_MASK);
|
||||
FSCHMD_TEMP_ALERT);
|
||||
}
|
||||
|
||||
for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) {
|
||||
|
@ -816,28 +1189,17 @@ static struct fschmd_data *fschmd_update_device(struct device *dev)
|
|||
FSCHMD_REG_FAN_MIN[data->kind][i]);
|
||||
|
||||
/* reset fan status if speed is back to > 0 */
|
||||
if ((data->fan_status[i] & FSCHMD_FAN_ALARM_MASK) &&
|
||||
if ((data->fan_status[i] & FSCHMD_FAN_ALARM) &&
|
||||
data->fan_act[i])
|
||||
i2c_smbus_write_byte_data(client,
|
||||
FSCHMD_REG_FAN_STATE[data->kind][i],
|
||||
FSCHMD_FAN_ALARM_MASK);
|
||||
FSCHMD_FAN_ALARM);
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
data->volt[i] = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_VOLT[i]);
|
||||
|
||||
data->global_control = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_CONTROL);
|
||||
|
||||
/* To be implemented in the future
|
||||
data->watchdog[0] = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_WDOG_PRESET);
|
||||
data->watchdog[1] = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_WDOG_STATE);
|
||||
data->watchdog[2] = i2c_smbus_read_byte_data(client,
|
||||
FSCHMD_REG_WDOG_CONTROL); */
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
@ -857,7 +1219,7 @@ static void __exit fschmd_exit(void)
|
|||
i2c_del_driver(&fschmd_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and "
|
||||
"Heimdall driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -490,6 +490,13 @@ static unsigned long chipset_ids[] = {
|
|||
0
|
||||
};
|
||||
|
||||
static struct pci_device_id i5k_amb_ids[] __devinitdata = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, i5k_amb_ids);
|
||||
|
||||
static int __devinit i5k_amb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i5k_amb_data *data;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
IT8712F Super I/O chip w/LPC interface
|
||||
IT8716F Super I/O chip w/LPC interface
|
||||
IT8718F Super I/O chip w/LPC interface
|
||||
IT8720F Super I/O chip w/LPC interface
|
||||
IT8726F Super I/O chip w/LPC interface
|
||||
Sis950 A clone of the IT8705F
|
||||
|
||||
|
@ -48,11 +49,12 @@
|
|||
#include <linux/sysfs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRVNAME "it87"
|
||||
|
||||
enum chips { it87, it8712, it8716, it8718 };
|
||||
enum chips { it87, it8712, it8716, it8718, it8720 };
|
||||
|
||||
static unsigned short force_id;
|
||||
module_param(force_id, ushort, 0);
|
||||
|
@ -64,7 +66,10 @@ static struct platform_device *pdev;
|
|||
#define DEV 0x07 /* Register: Logical device select */
|
||||
#define VAL 0x2f /* The value to read/write */
|
||||
#define PME 0x04 /* The device with the fan registers in it */
|
||||
#define GPIO 0x07 /* The device with the IT8718F VID value in it */
|
||||
|
||||
/* The device with the IT8718F/IT8720F VID value in it */
|
||||
#define GPIO 0x07
|
||||
|
||||
#define DEVID 0x20 /* Register: Device ID */
|
||||
#define DEVREV 0x22 /* Register: Device Revision */
|
||||
|
||||
|
@ -113,6 +118,7 @@ superio_exit(void)
|
|||
#define IT8705F_DEVID 0x8705
|
||||
#define IT8716F_DEVID 0x8716
|
||||
#define IT8718F_DEVID 0x8718
|
||||
#define IT8720F_DEVID 0x8720
|
||||
#define IT8726F_DEVID 0x8726
|
||||
#define IT87_ACT_REG 0x30
|
||||
#define IT87_BASE_REG 0x60
|
||||
|
@ -150,8 +156,8 @@ static int fix_pwm_polarity;
|
|||
#define IT87_REG_ALARM2 0x02
|
||||
#define IT87_REG_ALARM3 0x03
|
||||
|
||||
/* The IT8718F has the VID value in a different register, in Super-I/O
|
||||
configuration space. */
|
||||
/* The IT8718F and IT8720F have the VID value in a different register, in
|
||||
Super-I/O configuration space. */
|
||||
#define IT87_REG_VID 0x0a
|
||||
/* The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b
|
||||
for fan divisors. Later IT8712F revisions must use 16-bit tachometer
|
||||
|
@ -282,7 +288,8 @@ static inline int has_16bit_fans(const struct it87_data *data)
|
|||
return (data->type == it87 && data->revision >= 0x03)
|
||||
|| (data->type == it8712 && data->revision >= 0x08)
|
||||
|| data->type == it8716
|
||||
|| data->type == it8718;
|
||||
|| data->type == it8718
|
||||
|| data->type == it8720;
|
||||
}
|
||||
|
||||
static int it87_probe(struct platform_device *pdev);
|
||||
|
@ -992,6 +999,9 @@ static int __init it87_find(unsigned short *address,
|
|||
case IT8718F_DEVID:
|
||||
sio_data->type = it8718;
|
||||
break;
|
||||
case IT8720F_DEVID:
|
||||
sio_data->type = it8720;
|
||||
break;
|
||||
case 0xffff: /* No device at all */
|
||||
goto exit;
|
||||
default:
|
||||
|
@ -1022,7 +1032,8 @@ static int __init it87_find(unsigned short *address,
|
|||
int reg;
|
||||
|
||||
superio_select(GPIO);
|
||||
if (chip_type == it8718)
|
||||
if ((chip_type == it8718) ||
|
||||
(chip_type == it8720))
|
||||
sio_data->vid_value = superio_inb(IT87_SIO_VID_REG);
|
||||
|
||||
reg = superio_inb(IT87_SIO_PINX2_REG);
|
||||
|
@ -1068,6 +1079,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
|
|||
"it8712",
|
||||
"it8716",
|
||||
"it8718",
|
||||
"it8720",
|
||||
};
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
|
@ -1226,7 +1238,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (data->type == it8712 || data->type == it8716
|
||||
|| data->type == it8718) {
|
||||
|| data->type == it8718 || data->type == it8720) {
|
||||
data->vrm = vid_which_vrm();
|
||||
/* VID reading from Super-I/O config space if available */
|
||||
data->vid = sio_data->vid_value;
|
||||
|
@ -1374,7 +1386,7 @@ static void __devinit it87_init_device(struct platform_device *pdev)
|
|||
it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127);
|
||||
}
|
||||
|
||||
/* Check if temperature channnels are reset manually or by some reason */
|
||||
/* Check if temperature channels are reset manually or by some reason */
|
||||
tmp = it87_read_value(data, IT87_REG_TEMP_ENABLE);
|
||||
if ((tmp & 0x3f) == 0) {
|
||||
/* Temp1,Temp3=thermistor; Temp2=thermal diode */
|
||||
|
@ -1513,7 +1525,8 @@ static struct it87_data *it87_update_device(struct device *dev)
|
|||
|
||||
data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE);
|
||||
/* The 8705 does not have VID capability.
|
||||
The 8718 does not use IT87_REG_VID for the same purpose. */
|
||||
The 8718 and the 8720 don't use IT87_REG_VID for the
|
||||
same purpose. */
|
||||
if (data->type == it8712 || data->type == it8716) {
|
||||
data->vid = it87_read_value(data, IT87_REG_VID);
|
||||
/* The older IT8712F revisions had only 5 VID pins,
|
||||
|
@ -1540,6 +1553,10 @@ static int __init it87_device_add(unsigned short address,
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
@ -1608,7 +1625,7 @@ static void __exit sm_it87_exit(void)
|
|||
|
||||
MODULE_AUTHOR("Chris Gauthron, "
|
||||
"Jean Delvare <khali@linux-fr.org>");
|
||||
MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8726F, SiS950 driver");
|
||||
MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver");
|
||||
module_param(update_vbat, bool, 0);
|
||||
MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
|
||||
module_param(fix_pwm_polarity, bool, 0);
|
||||
|
|
|
@ -37,9 +37,13 @@
|
|||
|
||||
#define DRVNAME "lm70"
|
||||
|
||||
#define LM70_CHIP_LM70 0 /* original NS LM70 */
|
||||
#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */
|
||||
|
||||
struct lm70 {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex lock;
|
||||
unsigned int chip;
|
||||
};
|
||||
|
||||
/* sysfs hook function */
|
||||
|
@ -47,7 +51,7 @@ static ssize_t lm70_sense_temp(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
int status, val;
|
||||
int status, val = 0;
|
||||
u8 rxbuf[2];
|
||||
s16 raw=0;
|
||||
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
|
||||
|
@ -65,12 +69,12 @@ static ssize_t lm70_sense_temp(struct device *dev,
|
|||
"spi_write_then_read failed with status %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]);
|
||||
|
||||
raw = (rxbuf[1] << 8) + rxbuf[0];
|
||||
dev_dbg(dev, "raw=0x%x\n", raw);
|
||||
raw = (rxbuf[0] << 8) + rxbuf[1];
|
||||
dev_dbg(dev, "rxbuf[0] : 0x%02x rxbuf[1] : 0x%02x raw=0x%04x\n",
|
||||
rxbuf[0], rxbuf[1], raw);
|
||||
|
||||
/*
|
||||
* LM70:
|
||||
* The "raw" temperature read into rxbuf[] is a 16-bit signed 2's
|
||||
* complement value. Only the MSB 11 bits (1 sign + 10 temperature
|
||||
* bits) are meaningful; the LSB 5 bits are to be discarded.
|
||||
|
@ -80,8 +84,21 @@ static ssize_t lm70_sense_temp(struct device *dev,
|
|||
* by 0.25. Also multiply by 1000 to represent in millidegrees
|
||||
* Celsius.
|
||||
* So it's equivalent to multiplying by 0.25 * 1000 = 250.
|
||||
*
|
||||
* TMP121/TMP123:
|
||||
* 13 bits of 2's complement data, discard LSB 3 bits,
|
||||
* resolution 0.0625 degrees celsius.
|
||||
*/
|
||||
val = ((int)raw/32) * 250;
|
||||
switch (p_lm70->chip) {
|
||||
case LM70_CHIP_LM70:
|
||||
val = ((int)raw / 32) * 250;
|
||||
break;
|
||||
|
||||
case LM70_CHIP_TMP121:
|
||||
val = ((int)raw / 8) * 625 / 10;
|
||||
break;
|
||||
}
|
||||
|
||||
status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */
|
||||
out:
|
||||
mutex_unlock(&p_lm70->lock);
|
||||
|
@ -93,27 +110,39 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
|
|||
static ssize_t lm70_show_name(struct device *dev, struct device_attribute
|
||||
*devattr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "lm70\n");
|
||||
struct lm70 *p_lm70 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
switch (p_lm70->chip) {
|
||||
case LM70_CHIP_LM70:
|
||||
ret = sprintf(buf, "lm70\n");
|
||||
break;
|
||||
case LM70_CHIP_TMP121:
|
||||
ret = sprintf(buf, "tmp121\n");
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int __devinit lm70_probe(struct spi_device *spi)
|
||||
static int __devinit common_probe(struct spi_device *spi, int chip)
|
||||
{
|
||||
struct lm70 *p_lm70;
|
||||
int status;
|
||||
|
||||
/* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */
|
||||
if ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE))
|
||||
return -EINVAL;
|
||||
/* NOTE: we assume 8-bit words, and convert to 16 bits manually */
|
||||
|
||||
p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL);
|
||||
if (!p_lm70)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&p_lm70->lock);
|
||||
p_lm70->chip = chip;
|
||||
|
||||
/* sysfs hook */
|
||||
p_lm70->hwmon_dev = hwmon_device_register(&spi->dev);
|
||||
|
@ -141,6 +170,24 @@ out_dev_reg_failed:
|
|||
return status;
|
||||
}
|
||||
|
||||
static int __devinit lm70_probe(struct spi_device *spi)
|
||||
{
|
||||
/* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */
|
||||
if ((spi->mode & (SPI_CPOL | SPI_CPHA)) || !(spi->mode & SPI_3WIRE))
|
||||
return -EINVAL;
|
||||
|
||||
return common_probe(spi, LM70_CHIP_LM70);
|
||||
}
|
||||
|
||||
static int __devinit tmp121_probe(struct spi_device *spi)
|
||||
{
|
||||
/* signaling is SPI_MODE_0 with only MISO connected */
|
||||
if (spi->mode & (SPI_CPOL | SPI_CPHA))
|
||||
return -EINVAL;
|
||||
|
||||
return common_probe(spi, LM70_CHIP_TMP121);
|
||||
}
|
||||
|
||||
static int __devexit lm70_remove(struct spi_device *spi)
|
||||
{
|
||||
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
|
||||
|
@ -154,6 +201,15 @@ static int __devexit lm70_remove(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver tmp121_driver = {
|
||||
.driver = {
|
||||
.name = "tmp121",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tmp121_probe,
|
||||
.remove = __devexit_p(lm70_remove),
|
||||
};
|
||||
|
||||
static struct spi_driver lm70_driver = {
|
||||
.driver = {
|
||||
.name = "lm70",
|
||||
|
@ -165,17 +221,26 @@ static struct spi_driver lm70_driver = {
|
|||
|
||||
static int __init init_lm70(void)
|
||||
{
|
||||
return spi_register_driver(&lm70_driver);
|
||||
int ret = spi_register_driver(&lm70_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_register_driver(&tmp121_driver);
|
||||
if (ret)
|
||||
spi_unregister_driver(&lm70_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cleanup_lm70(void)
|
||||
{
|
||||
spi_unregister_driver(&lm70_driver);
|
||||
spi_unregister_driver(&tmp121_driver);
|
||||
}
|
||||
|
||||
module_init(init_lm70);
|
||||
module_exit(cleanup_lm70);
|
||||
|
||||
MODULE_AUTHOR("Kaiwan N Billimoria");
|
||||
MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver");
|
||||
MODULE_DESCRIPTION("NS LM70 / TI TMP121/TMP123 Linux driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
|
||||
*
|
||||
* Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This driver is based on the ds1621 and ina209 drivers.
|
||||
*
|
||||
* Datasheet:
|
||||
* http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
/* Valid addresses are 0x20 - 0x3f
|
||||
*
|
||||
* For now, we do not probe, since some of these addresses
|
||||
* are known to be unfriendly to probing */
|
||||
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(ltc4245);
|
||||
|
||||
/* Here are names of the chip's registers (a.k.a. commands) */
|
||||
enum ltc4245_cmd {
|
||||
LTC4245_STATUS = 0x00, /* readonly */
|
||||
LTC4245_ALERT = 0x01,
|
||||
LTC4245_CONTROL = 0x02,
|
||||
LTC4245_ON = 0x03,
|
||||
LTC4245_FAULT1 = 0x04,
|
||||
LTC4245_FAULT2 = 0x05,
|
||||
LTC4245_GPIO = 0x06,
|
||||
LTC4245_ADCADR = 0x07,
|
||||
|
||||
LTC4245_12VIN = 0x10,
|
||||
LTC4245_12VSENSE = 0x11,
|
||||
LTC4245_12VOUT = 0x12,
|
||||
LTC4245_5VIN = 0x13,
|
||||
LTC4245_5VSENSE = 0x14,
|
||||
LTC4245_5VOUT = 0x15,
|
||||
LTC4245_3VIN = 0x16,
|
||||
LTC4245_3VSENSE = 0x17,
|
||||
LTC4245_3VOUT = 0x18,
|
||||
LTC4245_VEEIN = 0x19,
|
||||
LTC4245_VEESENSE = 0x1a,
|
||||
LTC4245_VEEOUT = 0x1b,
|
||||
LTC4245_GPIOADC1 = 0x1c,
|
||||
LTC4245_GPIOADC2 = 0x1d,
|
||||
LTC4245_GPIOADC3 = 0x1e,
|
||||
};
|
||||
|
||||
struct ltc4245_data {
|
||||
struct device *hwmon_dev;
|
||||
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* Control registers */
|
||||
u8 cregs[0x08];
|
||||
|
||||
/* Voltage registers */
|
||||
u8 vregs[0x0f];
|
||||
};
|
||||
|
||||
static struct ltc4245_data *ltc4245_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ltc4245_data *data = i2c_get_clientdata(client);
|
||||
s32 val;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
|
||||
dev_dbg(&client->dev, "Starting ltc4245 update\n");
|
||||
|
||||
/* Read control registers -- 0x00 to 0x07 */
|
||||
for (i = 0; i < ARRAY_SIZE(data->cregs); i++) {
|
||||
val = i2c_smbus_read_byte_data(client, i);
|
||||
if (unlikely(val < 0))
|
||||
data->cregs[i] = 0;
|
||||
else
|
||||
data->cregs[i] = val;
|
||||
}
|
||||
|
||||
/* Read voltage registers -- 0x10 to 0x1f */
|
||||
for (i = 0; i < ARRAY_SIZE(data->vregs); i++) {
|
||||
val = i2c_smbus_read_byte_data(client, i+0x10);
|
||||
if (unlikely(val < 0))
|
||||
data->vregs[i] = 0;
|
||||
else
|
||||
data->vregs[i] = val;
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Return the voltage from the given register in millivolts */
|
||||
static int ltc4245_get_voltage(struct device *dev, u8 reg)
|
||||
{
|
||||
struct ltc4245_data *data = ltc4245_update_device(dev);
|
||||
const u8 regval = data->vregs[reg - 0x10];
|
||||
u32 voltage = 0;
|
||||
|
||||
switch (reg) {
|
||||
case LTC4245_12VIN:
|
||||
case LTC4245_12VOUT:
|
||||
voltage = regval * 55;
|
||||
break;
|
||||
case LTC4245_5VIN:
|
||||
case LTC4245_5VOUT:
|
||||
voltage = regval * 22;
|
||||
break;
|
||||
case LTC4245_3VIN:
|
||||
case LTC4245_3VOUT:
|
||||
voltage = regval * 15;
|
||||
break;
|
||||
case LTC4245_VEEIN:
|
||||
case LTC4245_VEEOUT:
|
||||
voltage = regval * -55;
|
||||
break;
|
||||
case LTC4245_GPIOADC1:
|
||||
case LTC4245_GPIOADC2:
|
||||
case LTC4245_GPIOADC3:
|
||||
voltage = regval * 10;
|
||||
break;
|
||||
default:
|
||||
/* If we get here, the developer messed up */
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
/* Return the current in the given sense register in milliAmperes */
|
||||
static unsigned int ltc4245_get_current(struct device *dev, u8 reg)
|
||||
{
|
||||
struct ltc4245_data *data = ltc4245_update_device(dev);
|
||||
const u8 regval = data->vregs[reg - 0x10];
|
||||
unsigned int voltage;
|
||||
unsigned int curr;
|
||||
|
||||
/* The strange looking conversions that follow are fixed-point
|
||||
* math, since we cannot do floating point in the kernel.
|
||||
*
|
||||
* Step 1: convert sense register to microVolts
|
||||
* Step 2: convert voltage to milliAmperes
|
||||
*
|
||||
* If you play around with the V=IR equation, you come up with
|
||||
* the following: X uV / Y mOhm == Z mA
|
||||
*
|
||||
* With the resistors that are fractions of a milliOhm, we multiply
|
||||
* the voltage and resistance by 10, to shift the decimal point.
|
||||
* Now we can use the normal division operator again.
|
||||
*/
|
||||
|
||||
switch (reg) {
|
||||
case LTC4245_12VSENSE:
|
||||
voltage = regval * 250; /* voltage in uV */
|
||||
curr = voltage / 50; /* sense resistor 50 mOhm */
|
||||
break;
|
||||
case LTC4245_5VSENSE:
|
||||
voltage = regval * 125; /* voltage in uV */
|
||||
curr = (voltage * 10) / 35; /* sense resistor 3.5 mOhm */
|
||||
break;
|
||||
case LTC4245_3VSENSE:
|
||||
voltage = regval * 125; /* voltage in uV */
|
||||
curr = (voltage * 10) / 25; /* sense resistor 2.5 mOhm */
|
||||
break;
|
||||
case LTC4245_VEESENSE:
|
||||
voltage = regval * 250; /* voltage in uV */
|
||||
curr = voltage / 100; /* sense resistor 100 mOhm */
|
||||
break;
|
||||
default:
|
||||
/* If we get here, the developer messed up */
|
||||
WARN_ON_ONCE(1);
|
||||
curr = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
static ssize_t ltc4245_show_voltage(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
const int voltage = ltc4245_get_voltage(dev, attr->index);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
|
||||
}
|
||||
|
||||
static ssize_t ltc4245_show_current(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
const unsigned int curr = ltc4245_get_current(dev, attr->index);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", curr);
|
||||
}
|
||||
|
||||
static ssize_t ltc4245_show_power(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
const unsigned int curr = ltc4245_get_current(dev, attr->index);
|
||||
const int output_voltage = ltc4245_get_voltage(dev, attr->index+1);
|
||||
|
||||
/* current in mA * voltage in mV == power in uW */
|
||||
const unsigned int power = abs(output_voltage * curr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", power);
|
||||
}
|
||||
|
||||
static ssize_t ltc4245_show_alarm(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
|
||||
struct ltc4245_data *data = ltc4245_update_device(dev);
|
||||
const u8 reg = data->cregs[attr->index];
|
||||
const u32 mask = attr->nr;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 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 LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \
|
||||
static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
|
||||
ltc4245_show_voltage, NULL, ltc4245_cmd_idx)
|
||||
|
||||
#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \
|
||||
static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
|
||||
ltc4245_show_current, NULL, ltc4245_cmd_idx)
|
||||
|
||||
#define LTC4245_POWER(name, ltc4245_cmd_idx) \
|
||||
static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
|
||||
ltc4245_show_power, NULL, ltc4245_cmd_idx)
|
||||
|
||||
#define LTC4245_ALARM(name, mask, reg) \
|
||||
static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
|
||||
ltc4245_show_alarm, NULL, (mask), reg)
|
||||
|
||||
/* Construct a sensor_device_attribute structure for each register */
|
||||
|
||||
/* Input voltages */
|
||||
LTC4245_VOLTAGE(in1_input, LTC4245_12VIN);
|
||||
LTC4245_VOLTAGE(in2_input, LTC4245_5VIN);
|
||||
LTC4245_VOLTAGE(in3_input, LTC4245_3VIN);
|
||||
LTC4245_VOLTAGE(in4_input, LTC4245_VEEIN);
|
||||
|
||||
/* Input undervoltage alarms */
|
||||
LTC4245_ALARM(in1_min_alarm, (1 << 0), LTC4245_FAULT1);
|
||||
LTC4245_ALARM(in2_min_alarm, (1 << 1), LTC4245_FAULT1);
|
||||
LTC4245_ALARM(in3_min_alarm, (1 << 2), LTC4245_FAULT1);
|
||||
LTC4245_ALARM(in4_min_alarm, (1 << 3), LTC4245_FAULT1);
|
||||
|
||||
/* Currents (via sense resistor) */
|
||||
LTC4245_CURRENT(curr1_input, LTC4245_12VSENSE);
|
||||
LTC4245_CURRENT(curr2_input, LTC4245_5VSENSE);
|
||||
LTC4245_CURRENT(curr3_input, LTC4245_3VSENSE);
|
||||
LTC4245_CURRENT(curr4_input, LTC4245_VEESENSE);
|
||||
|
||||
/* Overcurrent alarms */
|
||||
LTC4245_ALARM(curr1_max_alarm, (1 << 4), LTC4245_FAULT1);
|
||||
LTC4245_ALARM(curr2_max_alarm, (1 << 5), LTC4245_FAULT1);
|
||||
LTC4245_ALARM(curr3_max_alarm, (1 << 6), LTC4245_FAULT1);
|
||||
LTC4245_ALARM(curr4_max_alarm, (1 << 7), LTC4245_FAULT1);
|
||||
|
||||
/* Output voltages */
|
||||
LTC4245_VOLTAGE(in5_input, LTC4245_12VOUT);
|
||||
LTC4245_VOLTAGE(in6_input, LTC4245_5VOUT);
|
||||
LTC4245_VOLTAGE(in7_input, LTC4245_3VOUT);
|
||||
LTC4245_VOLTAGE(in8_input, LTC4245_VEEOUT);
|
||||
|
||||
/* Power Bad alarms */
|
||||
LTC4245_ALARM(in5_min_alarm, (1 << 0), LTC4245_FAULT2);
|
||||
LTC4245_ALARM(in6_min_alarm, (1 << 1), LTC4245_FAULT2);
|
||||
LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2);
|
||||
LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2);
|
||||
|
||||
/* GPIO voltages */
|
||||
LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC1);
|
||||
LTC4245_VOLTAGE(in10_input, LTC4245_GPIOADC2);
|
||||
LTC4245_VOLTAGE(in11_input, LTC4245_GPIOADC3);
|
||||
|
||||
/* Power Consumption (virtual) */
|
||||
LTC4245_POWER(power1_input, LTC4245_12VSENSE);
|
||||
LTC4245_POWER(power2_input, LTC4245_5VSENSE);
|
||||
LTC4245_POWER(power3_input, LTC4245_3VSENSE);
|
||||
LTC4245_POWER(power4_input, LTC4245_VEESENSE);
|
||||
|
||||
/* Finally, construct an array of pointers to members of the above objects,
|
||||
* as required for sysfs_create_group()
|
||||
*/
|
||||
static struct attribute *ltc4245_attributes[] = {
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_min_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr4_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_curr3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_curr4_max_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in7_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in8_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in5_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in7_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in8_min_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in9_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in10_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in11_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_power1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power4_input.dev_attr.attr,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ltc4245_group = {
|
||||
.attrs = ltc4245_attributes,
|
||||
};
|
||||
|
||||
static int ltc4245_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ltc4245_data *data;
|
||||
int ret;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto out_kzalloc;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the LTC4245 chip */
|
||||
/* TODO */
|
||||
|
||||
/* Register sysfs hooks */
|
||||
ret = sysfs_create_group(&client->dev.kobj, <c4245_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, <c4245_group);
|
||||
out_sysfs_create_group:
|
||||
kfree(data);
|
||||
out_kzalloc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc4245_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ltc4245_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, <c4245_group);
|
||||
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check that some bits in a control register appear at all possible
|
||||
* locations without changing value
|
||||
*
|
||||
* @client: the i2c client to use
|
||||
* @reg: the register to read
|
||||
* @bits: the bits to check (0xff checks all bits,
|
||||
* 0x03 checks only the last two bits)
|
||||
*
|
||||
* return -ERRNO if the register read failed
|
||||
* return -ENODEV if the register value doesn't stay constant at all
|
||||
* possible addresses
|
||||
*
|
||||
* return 0 for success
|
||||
*/
|
||||
static int ltc4245_check_control_reg(struct i2c_client *client, u8 reg, u8 bits)
|
||||
{
|
||||
int i;
|
||||
s32 v, voff1, voff2;
|
||||
|
||||
/* Read register and check for error */
|
||||
v = i2c_smbus_read_byte_data(client, reg);
|
||||
if (v < 0)
|
||||
return v;
|
||||
|
||||
v &= bits;
|
||||
|
||||
for (i = 0x00; i < 0xff; i += 0x20) {
|
||||
|
||||
voff1 = i2c_smbus_read_byte_data(client, reg + i);
|
||||
if (voff1 < 0)
|
||||
return voff1;
|
||||
|
||||
voff2 = i2c_smbus_read_byte_data(client, reg + i + 0x08);
|
||||
if (voff2 < 0)
|
||||
return voff2;
|
||||
|
||||
voff1 &= bits;
|
||||
voff2 &= bits;
|
||||
|
||||
if (v != voff1 || v != voff2)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc4245_detect(struct i2c_client *client,
|
||||
int kind,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
if (kind < 0) { /* probed detection - check the chip type */
|
||||
s32 v; /* 8 bits from the chip, or -ERRNO */
|
||||
|
||||
/* Chip registers 0x00-0x07 are control registers
|
||||
* Chip registers 0x10-0x1f are data registers
|
||||
*
|
||||
* Address bits b7-b5 are ignored. This makes the chip "repeat"
|
||||
* in steps of 0x20. Any control registers should appear with
|
||||
* the same values across all duplicated addresses.
|
||||
*
|
||||
* Register 0x02 bit b2 is reserved, expect 0
|
||||
* Register 0x07 bits b7 to b4 are reserved, expect 0
|
||||
*
|
||||
* Registers 0x01, 0x02 are control registers and should not
|
||||
* change on their own.
|
||||
*
|
||||
* Register 0x06 bits b6 and b7 are control bits, and should
|
||||
* not change on their own.
|
||||
*
|
||||
* Register 0x07 bits b3 to b0 are control bits, and should
|
||||
* not change on their own.
|
||||
*/
|
||||
|
||||
/* read register 0x02 reserved bit, expect 0 */
|
||||
v = i2c_smbus_read_byte_data(client, LTC4245_CONTROL);
|
||||
if (v < 0 || (v & 0x04) != 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* read register 0x07 reserved bits, expect 0 */
|
||||
v = i2c_smbus_read_byte_data(client, LTC4245_ADCADR);
|
||||
if (v < 0 || (v & 0xf0) != 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* check that the alert register appears at all locations */
|
||||
if (ltc4245_check_control_reg(client, LTC4245_ALERT, 0xff))
|
||||
return -ENODEV;
|
||||
|
||||
/* check that the control register appears at all locations */
|
||||
if (ltc4245_check_control_reg(client, LTC4245_CONTROL, 0xff))
|
||||
return -ENODEV;
|
||||
|
||||
/* check that register 0x06 bits b6 and b7 stay constant */
|
||||
if (ltc4245_check_control_reg(client, LTC4245_GPIO, 0xc0))
|
||||
return -ENODEV;
|
||||
|
||||
/* check that register 0x07 bits b3-b0 stay constant */
|
||||
if (ltc4245_check_control_reg(client, LTC4245_ADCADR, 0x0f))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(info->type, "ltc4245", I2C_NAME_SIZE);
|
||||
dev_info(&adapter->dev, "ltc4245 %s at address 0x%02x\n",
|
||||
kind < 0 ? "probed" : "forced",
|
||||
client->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc4245_id[] = {
|
||||
{ "ltc4245", ltc4245 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc4245_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ltc4245_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ltc4245",
|
||||
},
|
||||
.probe = ltc4245_probe,
|
||||
.remove = ltc4245_remove,
|
||||
.id_table = ltc4245_id,
|
||||
.detect = ltc4245_detect,
|
||||
.address_data = &addr_data,
|
||||
};
|
||||
|
||||
static int __init ltc4245_init(void)
|
||||
{
|
||||
return i2c_add_driver(<c4245_driver);
|
||||
}
|
||||
|
||||
static void __exit ltc4245_exit(void)
|
||||
{
|
||||
i2c_del_driver(<c4245_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
|
||||
MODULE_DESCRIPTION("LTC4245 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ltc4245_init);
|
||||
module_exit(ltc4245_exit);
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static u8 devid;
|
||||
|
@ -1627,6 +1628,11 @@ static int __init pc87360_device_add(unsigned short address)
|
|||
continue;
|
||||
res.start = extra_isa[i];
|
||||
res.end = extra_isa[i] + PC87360_EXTENT - 1;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit_device_put;
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "pc87360: Device resource[%d] "
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
|
@ -524,6 +525,10 @@ static int __init pc87427_device_add(unsigned short address)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include <linux/jiffies.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
|
@ -727,6 +728,10 @@ static int __devinit sis5595_device_add(unsigned short address)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc("sis5595", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
|
@ -303,6 +304,10 @@ static int __init smsc47b397_device_add(unsigned short address)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
|
@ -705,6 +706,10 @@ static int __init smsc47m1_device_add(unsigned short address,
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
|
@ -783,6 +784,10 @@ static int __devinit via686a_device_add(unsigned short address)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc("via686a", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static int uch_config = -1;
|
||||
|
@ -1259,6 +1260,10 @@ static int __init vt1211_device_add(unsigned short address)
|
|||
}
|
||||
|
||||
res.name = pdev->name;
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto EXIT;
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static int force_addr;
|
||||
|
@ -894,6 +895,10 @@ static int __devinit vt8231_device_add(unsigned short address)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc("vt8231", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
#include "lm75.h"
|
||||
|
||||
|
@ -502,7 +503,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
|||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
|
||||
/* pwmcfg, tolerance mapped for i=0, i=1 to same reg */
|
||||
if (i != 1) {
|
||||
pwmcfg = w83627ehf_read_value(data,
|
||||
W83627EHF_REG_PWM_ENABLE[i]);
|
||||
|
@ -1544,6 +1545,11 @@ static int __init sensors_w83627ehf_init(void)
|
|||
res.start = address + IOREGION_OFFSET;
|
||||
res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
|
||||
res.flags = IORESOURCE_IO;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
#include "lm75.h"
|
||||
|
||||
|
@ -1793,6 +1794,10 @@ static int __init w83627hf_device_add(unsigned short address,
|
|||
};
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
|
|
|
@ -58,7 +58,10 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
|||
0x2e, 0x2f, I2C_CLIENT_END };
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f);
|
||||
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
|
||||
|
||||
static unsigned short force_subclients[4];
|
||||
module_param_array(force_subclients, short, NULL, 0);
|
||||
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
|
||||
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
|
||||
|
||||
static int reset;
|
||||
|
|
|
@ -53,7 +53,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
|
|||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(w83791d);
|
||||
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
|
||||
|
||||
static unsigned short force_subclients[4];
|
||||
module_param_array(force_subclients, short, NULL, 0);
|
||||
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
|
||||
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
|
||||
|
||||
static int reset;
|
||||
|
|
|
@ -51,7 +51,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
|
|||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(w83792d);
|
||||
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
|
||||
|
||||
static unsigned short force_subclients[4];
|
||||
module_param_array(force_subclients, short, NULL, 0);
|
||||
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
|
||||
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
|
||||
|
||||
static int init;
|
||||
|
|
|
@ -42,7 +42,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
|
|||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(w83793);
|
||||
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
|
||||
|
||||
static unsigned short force_subclients[4];
|
||||
module_param_array(force_subclients, short, NULL, 0);
|
||||
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
|
||||
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
|
||||
|
||||
static int reset;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* spi_lm70llp.c - driver for lm70llp eval board for the LM70 sensor
|
||||
* spi_lm70llp.c - driver for LM70EVAL-LLP board for the LM70 sensor
|
||||
*
|
||||
* Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com>
|
||||
*
|
||||
|
@ -40,8 +40,12 @@
|
|||
* master controller driver. The hwmon/lm70 driver is a "SPI protocol
|
||||
* driver", layered on top of this one and usable without the lm70llp.
|
||||
*
|
||||
* Datasheet and Schematic:
|
||||
* The LM70 is a temperature sensor chip from National Semiconductor; its
|
||||
* datasheet is available at http://www.national.com/pf/LM/LM70.html
|
||||
* The schematic for this particular board (the LM70EVAL-LLP) is
|
||||
* available (on page 4) here:
|
||||
* http://www.national.com/appinfo/tempsensors/files/LM70LLPEVALmanual.pdf
|
||||
*
|
||||
* Also see Documentation/spi/spi-lm70llp. The SPI<->parport code here is
|
||||
* (heavily) based on spi-butterfly by David Brownell.
|
||||
|
@ -64,7 +68,7 @@
|
|||
*
|
||||
* Note that parport pin 13 actually gets inverted by the transistor
|
||||
* arrangement which lets either the parport or the LM70 drive the
|
||||
* SI/SO signal.
|
||||
* SI/SO signal (see the schematic for details).
|
||||
*/
|
||||
|
||||
#define DRVNAME "spi-lm70llp"
|
||||
|
@ -106,12 +110,16 @@ static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi)
|
|||
static inline void deassertCS(struct spi_lm70llp *pp)
|
||||
{
|
||||
u8 data = parport_read_data(pp->port);
|
||||
|
||||
data &= ~0x80; /* pull D7/SI-out low while de-asserted */
|
||||
parport_write_data(pp->port, data | nCS);
|
||||
}
|
||||
|
||||
static inline void assertCS(struct spi_lm70llp *pp)
|
||||
{
|
||||
u8 data = parport_read_data(pp->port);
|
||||
|
||||
data |= 0x80; /* pull D7/SI-out high so lm70 drives SO-in */
|
||||
parport_write_data(pp->port, data & ~nCS);
|
||||
}
|
||||
|
||||
|
@ -184,22 +192,7 @@ static void lm70_chipselect(struct spi_device *spi, int value)
|
|||
*/
|
||||
static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits)
|
||||
{
|
||||
static u32 sio=0;
|
||||
static int first_time=1;
|
||||
|
||||
/* First time: perform SPI bitbang and return the LSB of
|
||||
* the result of the SPI call.
|
||||
*/
|
||||
if (first_time) {
|
||||
sio = bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
|
||||
first_time=0;
|
||||
return (sio & 0x00ff);
|
||||
}
|
||||
/* Return the MSB of the result of the SPI call */
|
||||
else {
|
||||
first_time=1;
|
||||
return (sio >> 8);
|
||||
}
|
||||
return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
|
||||
}
|
||||
|
||||
static void spi_lm70llp_attach(struct parport *p)
|
||||
|
@ -293,10 +286,9 @@ static void spi_lm70llp_attach(struct parport *p)
|
|||
status = -ENODEV;
|
||||
goto out_bitbang_stop;
|
||||
}
|
||||
pp->spidev_lm70->bits_per_word = 16;
|
||||
pp->spidev_lm70->bits_per_word = 8;
|
||||
|
||||
lm70llp = pp;
|
||||
|
||||
return;
|
||||
|
||||
out_bitbang_stop:
|
||||
|
@ -326,7 +318,6 @@ static void spi_lm70llp_detach(struct parport *p)
|
|||
|
||||
/* power down */
|
||||
parport_write_data(pp->port, 0);
|
||||
msleep(10);
|
||||
|
||||
parport_release(pp->pd);
|
||||
parport_unregister_device(pp->pd);
|
||||
|
|
Loading…
Reference in New Issue