Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New driver: DA9055 Added/improved support for new chips in existing drivers: Z650/670, N550/570, ADS7830, AMD 16h family" * tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: hwmon: (da9055) Fix chan_mux[DA9055_ADC_ADCIN3] setting hwmon: DA9055 HWMON driver hwmon: (coretemp) List TjMax for Z650/670 and N550/570 hwmon: (coretemp) Drop N4xx, N5xx, D4xx, D5xx CPUs from tjmax table hwmon: (coretemp) Use model table instead of if/else to identify CPU models hwmon: da9052: Use da9052_reg_update for rmw operations hwmon: (coretemp) Drop dependency on PCI for TjMax detection on Atom CPUs hwmon: (ina2xx) use module_i2c_driver to simplify the code hwmon: (ads7828) add support for ADS7830 hwmon: (ads7828) driver cleanup x86,AMD: Power driver support for AMD's family 16h processors
This commit is contained in:
@@ -4,29 +4,47 @@ Kernel driver ads7828
|
|||||||
Supported chips:
|
Supported chips:
|
||||||
* Texas Instruments/Burr-Brown ADS7828
|
* Texas Instruments/Burr-Brown ADS7828
|
||||||
Prefix: 'ads7828'
|
Prefix: 'ads7828'
|
||||||
Addresses scanned: I2C 0x48, 0x49, 0x4a, 0x4b
|
|
||||||
Datasheet: Publicly available at the Texas Instruments website:
|
Datasheet: Publicly available at the Texas Instruments website:
|
||||||
http://focus.ti.com/lit/ds/symlink/ads7828.pdf
|
http://focus.ti.com/lit/ds/symlink/ads7828.pdf
|
||||||
|
|
||||||
|
* Texas Instruments ADS7830
|
||||||
|
Prefix: 'ads7830'
|
||||||
|
Datasheet: Publicly available at the Texas Instruments website:
|
||||||
|
http://focus.ti.com/lit/ds/symlink/ads7830.pdf
|
||||||
|
|
||||||
Authors:
|
Authors:
|
||||||
Steve Hardy <shardy@redhat.com>
|
Steve Hardy <shardy@redhat.com>
|
||||||
|
Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||||
|
|
||||||
Module Parameters
|
Platform data
|
||||||
-----------------
|
-------------
|
||||||
|
|
||||||
* se_input: bool (default Y)
|
The ads7828 driver accepts an optional ads7828_platform_data structure (defined
|
||||||
Single ended operation - set to N for differential mode
|
in include/linux/platform_data/ads7828.h). The structure fields are:
|
||||||
* int_vref: bool (default Y)
|
|
||||||
Operate with the internal 2.5V reference - set to N for external reference
|
* diff_input: (bool) Differential operation
|
||||||
* vref_mv: int (default 2500)
|
set to true for differential mode, false for default single ended mode.
|
||||||
If using an external reference, set this to the reference voltage in mV
|
|
||||||
|
* ext_vref: (bool) External reference
|
||||||
|
set to true if it operates with an external reference, false for default
|
||||||
|
internal reference.
|
||||||
|
|
||||||
|
* vref_mv: (unsigned int) Voltage reference
|
||||||
|
if using an external reference, set this to the reference voltage in mV,
|
||||||
|
otherwise it will default to the internal value (2500mV). This value will be
|
||||||
|
bounded with limits accepted by the chip, described in the datasheet.
|
||||||
|
|
||||||
|
If no structure is provided, the configuration defaults to single ended
|
||||||
|
operation and internal voltage reference (2.5V).
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
This driver implements support for the Texas Instruments ADS7828.
|
This driver implements support for the Texas Instruments ADS7828 and ADS7830.
|
||||||
|
|
||||||
This device is a 12-bit 8-channel A-D converter.
|
The ADS7828 device is a 12-bit 8-channel A/D converter, while the ADS7830 does
|
||||||
|
8-bit sampling.
|
||||||
|
|
||||||
It can operate in single ended mode (8 +ve inputs) or in differential mode,
|
It can operate in single ended mode (8 +ve inputs) or in differential mode,
|
||||||
where 4 differential pairs can be measured.
|
where 4 differential pairs can be measured.
|
||||||
@@ -34,3 +52,7 @@ where 4 differential pairs can be measured.
|
|||||||
The chip also has the facility to use an external voltage reference. This
|
The chip also has the facility to use an external voltage reference. This
|
||||||
may be required if your hardware supplies the ADS7828 from a 5V supply, see
|
may be required if your hardware supplies the ADS7828 from a 5V supply, see
|
||||||
the datasheet for more details.
|
the datasheet for more details.
|
||||||
|
|
||||||
|
There is no reliable way to identify this chip, so the driver will not scan
|
||||||
|
some addresses to try to auto-detect it. That means that you will have to
|
||||||
|
statically declare the device in the platform support code.
|
||||||
|
@@ -98,8 +98,10 @@ Process Processor TjMax(C)
|
|||||||
|
|
||||||
45nm Atom Processors
|
45nm Atom Processors
|
||||||
D525/510/425/410 100
|
D525/510/425/410 100
|
||||||
|
Z670/650 90
|
||||||
Z560/550/540/530P/530/520PT/520/515/510PT/510P 90
|
Z560/550/540/530P/530/520PT/520/515/510PT/510P 90
|
||||||
Z510/500 90
|
Z510/500 90
|
||||||
|
N570/550 100
|
||||||
N475/470/455/450 100
|
N475/470/455/450 100
|
||||||
N280/270 90
|
N280/270 90
|
||||||
330/230 125
|
330/230 125
|
||||||
|
47
Documentation/hwmon/da9055
Normal file
47
Documentation/hwmon/da9055
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
Supported chips:
|
||||||
|
* Dialog Semiconductors DA9055 PMIC
|
||||||
|
Prefix: 'da9055'
|
||||||
|
Datasheet: Datasheet is not publicly available.
|
||||||
|
|
||||||
|
Authors: David Dajun Chen <dchen@diasemi.com>
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The DA9055 provides an Analogue to Digital Converter (ADC) with 10 bits
|
||||||
|
resolution and track and hold circuitry combined with an analogue input
|
||||||
|
multiplexer. The analogue input multiplexer will allow conversion of up to 5
|
||||||
|
different inputs. The track and hold circuit ensures stable input voltages at
|
||||||
|
the input of the ADC during the conversion.
|
||||||
|
|
||||||
|
The ADC is used to measure the following inputs:
|
||||||
|
Channel 0: VDDOUT - measurement of the system voltage
|
||||||
|
Channel 1: ADC_IN1 - high impedance input (0 - 2.5V)
|
||||||
|
Channel 2: ADC_IN2 - high impedance input (0 - 2.5V)
|
||||||
|
Channel 3: ADC_IN3 - high impedance input (0 - 2.5V)
|
||||||
|
Channel 4: Internal Tjunc. - sense (internal temp. sensor)
|
||||||
|
|
||||||
|
By using sysfs attributes we can measure the system voltage VDDOUT,
|
||||||
|
chip junction temperature and auxiliary channels voltages.
|
||||||
|
|
||||||
|
Voltage Monitoring
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Voltages are sampled in a AUTO mode it can be manually sampled too and results
|
||||||
|
are stored in a 10 bit ADC.
|
||||||
|
|
||||||
|
The system voltage is calculated as:
|
||||||
|
Milli volt = ((ADC value * 1000) / 85) + 2500
|
||||||
|
|
||||||
|
The voltages on ADC channels 1, 2 and 3 are calculated as:
|
||||||
|
Milli volt = (ADC value * 1000) / 102
|
||||||
|
|
||||||
|
Temperature Monitoring
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Temperatures are sampled by a 10 bit ADC. Junction temperatures
|
||||||
|
are monitored by the ADC channels.
|
||||||
|
|
||||||
|
The junction temperature is calculated:
|
||||||
|
Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332
|
||||||
|
The junction temperature attribute is supported by the driver.
|
@@ -334,6 +334,16 @@ config SENSORS_DA9052_ADC
|
|||||||
This driver can also be built as module. If so, the module
|
This driver can also be built as module. If so, the module
|
||||||
will be called da9052-hwmon.
|
will be called da9052-hwmon.
|
||||||
|
|
||||||
|
config SENSORS_DA9055
|
||||||
|
tristate "Dialog Semiconductor DA9055 ADC"
|
||||||
|
depends on MFD_DA9055
|
||||||
|
help
|
||||||
|
If you say yes here you get support for ADC on the Dialog
|
||||||
|
Semiconductor DA9055 PMIC.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called da9055-hwmon.
|
||||||
|
|
||||||
config SENSORS_I5K_AMB
|
config SENSORS_I5K_AMB
|
||||||
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
|
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
@@ -455,7 +465,7 @@ config SENSORS_HIH6130
|
|||||||
|
|
||||||
config SENSORS_CORETEMP
|
config SENSORS_CORETEMP
|
||||||
tristate "Intel Core/Core2/Atom temperature sensor"
|
tristate "Intel Core/Core2/Atom temperature sensor"
|
||||||
depends on X86 && PCI
|
depends on X86
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the temperature
|
If you say yes here you get support for the temperature
|
||||||
sensor inside your CPU. Most of the family 6 CPUs
|
sensor inside your CPU. Most of the family 6 CPUs
|
||||||
@@ -1106,11 +1116,12 @@ config SENSORS_ADS1015
|
|||||||
will be called ads1015.
|
will be called ads1015.
|
||||||
|
|
||||||
config SENSORS_ADS7828
|
config SENSORS_ADS7828
|
||||||
tristate "Texas Instruments ADS7828"
|
tristate "Texas Instruments ADS7828 and compatibles"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
help
|
help
|
||||||
If you say yes here you get support for Texas Instruments ADS7828
|
If you say yes here you get support for Texas Instruments ADS7828 and
|
||||||
12-bit 8-channel ADC device.
|
ADS7830 8-channel A/D converters. ADS7828 resolution is 12-bit, while
|
||||||
|
it is 8-bit on ADS7830.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called ads7828.
|
will be called ads7828.
|
||||||
|
@@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
|
|||||||
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
||||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||||
obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
|
obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
|
||||||
|
obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
|
||||||
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
|
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
|
||||||
obj-$(CONFIG_SENSORS_DS620) += ds620.o
|
obj-$(CONFIG_SENSORS_DS620) += ds620.o
|
||||||
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* ads7828.c - lm_sensors driver for ads7828 12-bit 8-channel ADC
|
* ads7828.c - driver for TI ADS7828 8-channel A/D converter and compatibles
|
||||||
* (C) 2007 EADS Astrium
|
* (C) 2007 EADS Astrium
|
||||||
*
|
*
|
||||||
* This driver is based on the lm75 and other lm_sensors/hwmon drivers
|
* This driver is based on the lm75 and other lm_sensors/hwmon drivers
|
||||||
*
|
*
|
||||||
* Written by Steve Hardy <shardy@redhat.com>
|
* Written by Steve Hardy <shardy@redhat.com>
|
||||||
*
|
*
|
||||||
* Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.pdf
|
* ADS7830 support, by Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||||
|
*
|
||||||
|
* For further information, see the Documentation/hwmon/ads7828 file.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,63 +25,48 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/err.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/platform_data/ads7828.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
/* The ADS7828 registers */
|
/* The ADS7828 registers */
|
||||||
#define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */
|
#define ADS7828_NCH 8 /* 8 channels supported */
|
||||||
#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
|
#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
|
||||||
#define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */
|
#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */
|
||||||
#define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */
|
#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */
|
||||||
#define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */
|
|
||||||
#define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */
|
|
||||||
#define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */
|
|
||||||
#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */
|
#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */
|
||||||
|
#define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */
|
||||||
|
#define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */
|
||||||
|
|
||||||
/* Addresses to scan */
|
/* List of supported devices */
|
||||||
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
|
enum ads7828_chips { ads7828, ads7830 };
|
||||||
I2C_CLIENT_END };
|
|
||||||
|
|
||||||
/* Module parameters */
|
/* Client specific data */
|
||||||
static bool se_input = 1; /* Default is SE, 0 == diff */
|
|
||||||
static bool int_vref = 1; /* Default is internal ref ON */
|
|
||||||
static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */
|
|
||||||
module_param(se_input, bool, S_IRUGO);
|
|
||||||
module_param(int_vref, bool, S_IRUGO);
|
|
||||||
module_param(vref_mv, int, S_IRUGO);
|
|
||||||
|
|
||||||
/* Global Variables */
|
|
||||||
static u8 ads7828_cmd_byte; /* cmd byte without channel bits */
|
|
||||||
static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */
|
|
||||||
|
|
||||||
/* Each client has this additional data */
|
|
||||||
struct ads7828_data {
|
struct ads7828_data {
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
struct mutex update_lock; /* mutex protect updates */
|
struct mutex update_lock; /* Mutex protecting updates */
|
||||||
char valid; /* !=0 if following fields are valid */
|
unsigned long last_updated; /* Last updated time (in jiffies) */
|
||||||
unsigned long last_updated; /* In jiffies */
|
u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */
|
||||||
u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */
|
bool valid; /* Validity flag */
|
||||||
|
bool diff_input; /* Differential input */
|
||||||
|
bool ext_vref; /* External voltage reference */
|
||||||
|
unsigned int vref_mv; /* voltage reference value */
|
||||||
|
u8 cmd_byte; /* Command byte without channel bits */
|
||||||
|
unsigned int lsb_resol; /* Resolution of the ADC sample LSB */
|
||||||
|
s32 (*read_channel)(const struct i2c_client *client, u8 command);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Function declaration - necessary due to function dependencies */
|
/* Command byte C2,C1,C0 - see datasheet */
|
||||||
static int ads7828_detect(struct i2c_client *client,
|
static inline u8 ads7828_cmd_byte(u8 cmd, int ch)
|
||||||
struct i2c_board_info *info);
|
|
||||||
static int ads7828_probe(struct i2c_client *client,
|
|
||||||
const struct i2c_device_id *id);
|
|
||||||
|
|
||||||
static inline u8 channel_cmd_byte(int ch)
|
|
||||||
{
|
{
|
||||||
/* cmd byte C2,C1,C0 - see datasheet */
|
return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4);
|
||||||
u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4);
|
|
||||||
cmd |= ads7828_cmd_byte;
|
|
||||||
return cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update data for the device (all 8 channels) */
|
/* Update data for the device (all 8 channels) */
|
||||||
@@ -96,12 +83,11 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
|
|||||||
dev_dbg(&client->dev, "Starting ads7828 update\n");
|
dev_dbg(&client->dev, "Starting ads7828 update\n");
|
||||||
|
|
||||||
for (ch = 0; ch < ADS7828_NCH; ch++) {
|
for (ch = 0; ch < ADS7828_NCH; ch++) {
|
||||||
u8 cmd = channel_cmd_byte(ch);
|
u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch);
|
||||||
data->adc_input[ch] =
|
data->adc_input[ch] = data->read_channel(client, cmd);
|
||||||
i2c_smbus_read_word_swapped(client, cmd);
|
|
||||||
}
|
}
|
||||||
data->last_updated = jiffies;
|
data->last_updated = jiffies;
|
||||||
data->valid = 1;
|
data->valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&data->update_lock);
|
mutex_unlock(&data->update_lock);
|
||||||
@@ -110,28 +96,25 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* sysfs callback function */
|
/* sysfs callback function */
|
||||||
static ssize_t show_in(struct device *dev, struct device_attribute *da,
|
static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||||
struct ads7828_data *data = ads7828_update_device(dev);
|
struct ads7828_data *data = ads7828_update_device(dev);
|
||||||
/* Print value (in mV as specified in sysfs-interface documentation) */
|
unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] *
|
||||||
return sprintf(buf, "%d\n", (data->adc_input[attr->index] *
|
data->lsb_resol, 1000);
|
||||||
ads7828_lsb_resol)/1000);
|
|
||||||
|
return sprintf(buf, "%d\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define in_reg(offset)\
|
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0);
|
||||||
static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\
|
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1);
|
||||||
NULL, offset)
|
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ads7828_show_in, NULL, 2);
|
||||||
|
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ads7828_show_in, NULL, 3);
|
||||||
in_reg(0);
|
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4);
|
||||||
in_reg(1);
|
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5);
|
||||||
in_reg(2);
|
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6);
|
||||||
in_reg(3);
|
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7);
|
||||||
in_reg(4);
|
|
||||||
in_reg(5);
|
|
||||||
in_reg(6);
|
|
||||||
in_reg(7);
|
|
||||||
|
|
||||||
static struct attribute *ads7828_attributes[] = {
|
static struct attribute *ads7828_attributes[] = {
|
||||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||||
@@ -152,60 +135,9 @@ static const struct attribute_group ads7828_group = {
|
|||||||
static int ads7828_remove(struct i2c_client *client)
|
static int ads7828_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct ads7828_data *data = i2c_get_clientdata(client);
|
struct ads7828_data *data = i2c_get_clientdata(client);
|
||||||
|
|
||||||
hwmon_device_unregister(data->hwmon_dev);
|
hwmon_device_unregister(data->hwmon_dev);
|
||||||
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
|
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_device_id ads7828_id[] = {
|
|
||||||
{ "ads7828", 0 },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(i2c, ads7828_id);
|
|
||||||
|
|
||||||
/* This is the driver that will be inserted */
|
|
||||||
static struct i2c_driver ads7828_driver = {
|
|
||||||
.class = I2C_CLASS_HWMON,
|
|
||||||
.driver = {
|
|
||||||
.name = "ads7828",
|
|
||||||
},
|
|
||||||
.probe = ads7828_probe,
|
|
||||||
.remove = ads7828_remove,
|
|
||||||
.id_table = ads7828_id,
|
|
||||||
.detect = ads7828_detect,
|
|
||||||
.address_list = normal_i2c,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
|
||||||
static int ads7828_detect(struct i2c_client *client,
|
|
||||||
struct i2c_board_info *info)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *adapter = client->adapter;
|
|
||||||
int ch;
|
|
||||||
|
|
||||||
/* Check we have a valid client */
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now, we do the remaining detection. There is no identification
|
|
||||||
* dedicated register so attempt to sanity check using knowledge of
|
|
||||||
* the chip
|
|
||||||
* - Read from the 8 channel addresses
|
|
||||||
* - Check the top 4 bits of each result are not set (12 data bits)
|
|
||||||
*/
|
|
||||||
for (ch = 0; ch < ADS7828_NCH; ch++) {
|
|
||||||
u16 in_data;
|
|
||||||
u8 cmd = channel_cmd_byte(ch);
|
|
||||||
in_data = i2c_smbus_read_word_swapped(client, cmd);
|
|
||||||
if (in_data & 0xF000) {
|
|
||||||
pr_debug("%s : Doesn't look like an ads7828 device\n",
|
|
||||||
__func__);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strlcpy(info->type, "ads7828", I2C_NAME_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -213,6 +145,7 @@ static int ads7828_detect(struct i2c_client *client,
|
|||||||
static int ads7828_probe(struct i2c_client *client,
|
static int ads7828_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct ads7828_platform_data *pdata = client->dev.platform_data;
|
||||||
struct ads7828_data *data;
|
struct ads7828_data *data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@@ -221,10 +154,37 @@ static int ads7828_probe(struct i2c_client *client,
|
|||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (pdata) {
|
||||||
|
data->diff_input = pdata->diff_input;
|
||||||
|
data->ext_vref = pdata->ext_vref;
|
||||||
|
if (data->ext_vref)
|
||||||
|
data->vref_mv = pdata->vref_mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bound Vref with min/max values if it was provided */
|
||||||
|
if (data->vref_mv)
|
||||||
|
data->vref_mv = SENSORS_LIMIT(data->vref_mv,
|
||||||
|
ADS7828_EXT_VREF_MV_MIN,
|
||||||
|
ADS7828_EXT_VREF_MV_MAX);
|
||||||
|
else
|
||||||
|
data->vref_mv = ADS7828_INT_VREF_MV;
|
||||||
|
|
||||||
|
/* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */
|
||||||
|
if (id->driver_data == ads7828) {
|
||||||
|
data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096);
|
||||||
|
data->read_channel = i2c_smbus_read_word_swapped;
|
||||||
|
} else {
|
||||||
|
data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256);
|
||||||
|
data->read_channel = i2c_smbus_read_byte_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
|
||||||
|
if (!data->diff_input)
|
||||||
|
data->cmd_byte |= ADS7828_CMD_SD_SE;
|
||||||
|
|
||||||
i2c_set_clientdata(client, data);
|
i2c_set_clientdata(client, data);
|
||||||
mutex_init(&data->update_lock);
|
mutex_init(&data->update_lock);
|
||||||
|
|
||||||
/* Register sysfs hooks */
|
|
||||||
err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
|
err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
@@ -232,38 +192,35 @@ static int ads7828_probe(struct i2c_client *client,
|
|||||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||||
if (IS_ERR(data->hwmon_dev)) {
|
if (IS_ERR(data->hwmon_dev)) {
|
||||||
err = PTR_ERR(data->hwmon_dev);
|
err = PTR_ERR(data->hwmon_dev);
|
||||||
goto exit_remove;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exit_remove:
|
error:
|
||||||
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
|
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init sensors_ads7828_init(void)
|
static const struct i2c_device_id ads7828_device_ids[] = {
|
||||||
{
|
{ "ads7828", ads7828 },
|
||||||
/* Initialize the command byte according to module parameters */
|
{ "ads7830", ads7830 },
|
||||||
ads7828_cmd_byte = se_input ?
|
{ }
|
||||||
ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF;
|
};
|
||||||
ads7828_cmd_byte |= int_vref ?
|
MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
|
||||||
ADS7828_CMD_PD3 : ADS7828_CMD_PD1;
|
|
||||||
|
|
||||||
/* Calculate the LSB resolution */
|
static struct i2c_driver ads7828_driver = {
|
||||||
ads7828_lsb_resol = (vref_mv*1000)/4096;
|
.driver = {
|
||||||
|
.name = "ads7828",
|
||||||
|
},
|
||||||
|
|
||||||
return i2c_add_driver(&ads7828_driver);
|
.id_table = ads7828_device_ids,
|
||||||
}
|
.probe = ads7828_probe,
|
||||||
|
.remove = ads7828_remove,
|
||||||
|
};
|
||||||
|
|
||||||
static void __exit sensors_ads7828_exit(void)
|
module_i2c_driver(ads7828_driver);
|
||||||
{
|
|
||||||
i2c_del_driver(&ads7828_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
|
|
||||||
MODULE_DESCRIPTION("ADS7828 driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
|
||||||
module_init(sensors_ads7828_init);
|
MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles");
|
||||||
module_exit(sensors_ads7828_exit);
|
|
||||||
|
@@ -34,7 +34,6 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <asm/msr.h>
|
#include <asm/msr.h>
|
||||||
@@ -197,14 +196,6 @@ struct tjmax {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct tjmax __cpuinitconst tjmax_table[] = {
|
static const struct tjmax __cpuinitconst tjmax_table[] = {
|
||||||
{ "CPU D410", 100000 },
|
|
||||||
{ "CPU D425", 100000 },
|
|
||||||
{ "CPU D510", 100000 },
|
|
||||||
{ "CPU D525", 100000 },
|
|
||||||
{ "CPU N450", 100000 },
|
|
||||||
{ "CPU N455", 100000 },
|
|
||||||
{ "CPU N470", 100000 },
|
|
||||||
{ "CPU N475", 100000 },
|
|
||||||
{ "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */
|
{ "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */
|
||||||
{ "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */
|
{ "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */
|
||||||
{ "CPU CE4110", 110000 }, /* Model 0x1c, stepping 10 */
|
{ "CPU CE4110", 110000 }, /* Model 0x1c, stepping 10 */
|
||||||
@@ -212,6 +203,28 @@ static const struct tjmax __cpuinitconst tjmax_table[] = {
|
|||||||
{ "CPU CE4170", 110000 }, /* Model 0x1c, stepping 10 */
|
{ "CPU CE4170", 110000 }, /* Model 0x1c, stepping 10 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct tjmax_model {
|
||||||
|
u8 model;
|
||||||
|
u8 mask;
|
||||||
|
int tjmax;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ANY 0xff
|
||||||
|
|
||||||
|
static const struct tjmax_model __cpuinitconst tjmax_model_table[] = {
|
||||||
|
{ 0x1c, 10, 100000 }, /* D4xx, N4xx, D5xx, N5xx */
|
||||||
|
{ 0x1c, ANY, 90000 }, /* Z5xx, N2xx, possibly others
|
||||||
|
* Note: Also matches 230 and 330,
|
||||||
|
* which are covered by tjmax_table
|
||||||
|
*/
|
||||||
|
{ 0x26, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx)
|
||||||
|
* Note: TjMax for E6xxT is 110C, but CPU type
|
||||||
|
* is undetectable by software
|
||||||
|
*/
|
||||||
|
{ 0x27, ANY, 90000 }, /* Atom Medfield (Z2460) */
|
||||||
|
{ 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) */
|
||||||
|
};
|
||||||
|
|
||||||
static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id,
|
static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id,
|
||||||
struct device *dev)
|
struct device *dev)
|
||||||
{
|
{
|
||||||
@@ -222,7 +235,6 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id,
|
|||||||
int usemsr_ee = 1;
|
int usemsr_ee = 1;
|
||||||
int err;
|
int err;
|
||||||
u32 eax, edx;
|
u32 eax, edx;
|
||||||
struct pci_dev *host_bridge;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* explicit tjmax table entries override heuristics */
|
/* explicit tjmax table entries override heuristics */
|
||||||
@@ -231,32 +243,18 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id,
|
|||||||
return tjmax_table[i].tjmax;
|
return tjmax_table[i].tjmax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) {
|
||||||
|
const struct tjmax_model *tm = &tjmax_model_table[i];
|
||||||
|
if (c->x86_model == tm->model &&
|
||||||
|
(tm->mask == ANY || c->x86_mask == tm->mask))
|
||||||
|
return tm->tjmax;
|
||||||
|
}
|
||||||
|
|
||||||
/* Early chips have no MSR for TjMax */
|
/* Early chips have no MSR for TjMax */
|
||||||
|
|
||||||
if (c->x86_model == 0xf && c->x86_mask < 4)
|
if (c->x86_model == 0xf && c->x86_mask < 4)
|
||||||
usemsr_ee = 0;
|
usemsr_ee = 0;
|
||||||
|
|
||||||
/* Atom CPUs */
|
|
||||||
|
|
||||||
if (c->x86_model == 0x1c || c->x86_model == 0x26
|
|
||||||
|| c->x86_model == 0x27) {
|
|
||||||
usemsr_ee = 0;
|
|
||||||
|
|
||||||
host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
|
|
||||||
|
|
||||||
if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL
|
|
||||||
&& (host_bridge->device == 0xa000 /* NM10 based nettop */
|
|
||||||
|| host_bridge->device == 0xa010)) /* NM10 based netbook */
|
|
||||||
tjmax = 100000;
|
|
||||||
else
|
|
||||||
tjmax = 90000;
|
|
||||||
|
|
||||||
pci_dev_put(host_bridge);
|
|
||||||
} else if (c->x86_model == 0x36) {
|
|
||||||
usemsr_ee = 0;
|
|
||||||
tjmax = 100000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->x86_model > 0xe && usemsr_ee) {
|
if (c->x86_model > 0xe && usemsr_ee) {
|
||||||
u8 platform_id;
|
u8 platform_id;
|
||||||
|
|
||||||
|
@@ -60,30 +60,17 @@ static inline int vbbat_reg_to_mV(int value)
|
|||||||
return DIV_ROUND_CLOSEST(value * 2500, 512);
|
return DIV_ROUND_CLOSEST(value * 2500, 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int da9052_enable_vddout_channel(struct da9052 *da9052)
|
static inline int da9052_enable_vddout_channel(struct da9052 *da9052)
|
||||||
{
|
{
|
||||||
int ret;
|
return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
|
||||||
|
DA9052_ADCCONT_AUTOVDDEN,
|
||||||
ret = da9052_reg_read(da9052, DA9052_ADC_CONT_REG);
|
DA9052_ADCCONT_AUTOVDDEN);
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret |= DA9052_ADCCONT_AUTOVDDEN;
|
|
||||||
|
|
||||||
return da9052_reg_write(da9052, DA9052_ADC_CONT_REG, ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int da9052_disable_vddout_channel(struct da9052 *da9052)
|
static inline int da9052_disable_vddout_channel(struct da9052 *da9052)
|
||||||
{
|
{
|
||||||
int ret;
|
return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
|
||||||
|
DA9052_ADCCONT_AUTOVDDEN, 0);
|
||||||
ret = da9052_reg_read(da9052, DA9052_ADC_CONT_REG);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret &= ~DA9052_ADCCONT_AUTOVDDEN;
|
|
||||||
|
|
||||||
return da9052_reg_write(da9052, DA9052_ADC_CONT_REG, ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t da9052_read_vddout(struct device *dev,
|
static ssize_t da9052_read_vddout(struct device *dev,
|
||||||
|
336
drivers/hwmon/da9055-hwmon.c
Normal file
336
drivers/hwmon/da9055-hwmon.c
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* HWMON Driver for Dialog DA9055
|
||||||
|
*
|
||||||
|
* Copyright(c) 2012 Dialog Semiconductor Ltd.
|
||||||
|
*
|
||||||
|
* Author: David Dajun Chen <dchen@diasemi.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/hwmon-sysfs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
|
||||||
|
#include <linux/mfd/da9055/core.h>
|
||||||
|
#include <linux/mfd/da9055/reg.h>
|
||||||
|
|
||||||
|
#define DA9055_ADCIN_DIV 102
|
||||||
|
#define DA9055_VSYS_DIV 85
|
||||||
|
|
||||||
|
#define DA9055_ADC_VSYS 0
|
||||||
|
#define DA9055_ADC_ADCIN1 1
|
||||||
|
#define DA9055_ADC_ADCIN2 2
|
||||||
|
#define DA9055_ADC_ADCIN3 3
|
||||||
|
#define DA9055_ADC_TJUNC 4
|
||||||
|
|
||||||
|
struct da9055_hwmon {
|
||||||
|
struct da9055 *da9055;
|
||||||
|
struct device *class_device;
|
||||||
|
struct mutex hwmon_lock;
|
||||||
|
struct mutex irq_lock;
|
||||||
|
struct completion done;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const input_names[] = {
|
||||||
|
[DA9055_ADC_VSYS] = "VSYS",
|
||||||
|
[DA9055_ADC_ADCIN1] = "ADC IN1",
|
||||||
|
[DA9055_ADC_ADCIN2] = "ADC IN2",
|
||||||
|
[DA9055_ADC_ADCIN3] = "ADC IN3",
|
||||||
|
[DA9055_ADC_TJUNC] = "CHIP TEMP",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 chan_mux[DA9055_ADC_TJUNC + 1] = {
|
||||||
|
[DA9055_ADC_VSYS] = DA9055_ADC_MUX_VSYS,
|
||||||
|
[DA9055_ADC_ADCIN1] = DA9055_ADC_MUX_ADCIN1,
|
||||||
|
[DA9055_ADC_ADCIN2] = DA9055_ADC_MUX_ADCIN2,
|
||||||
|
[DA9055_ADC_ADCIN3] = DA9055_ADC_MUX_ADCIN3,
|
||||||
|
[DA9055_ADC_TJUNC] = DA9055_ADC_MUX_T_SENSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int da9055_adc_manual_read(struct da9055_hwmon *hwmon,
|
||||||
|
unsigned char channel)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned short calc_data;
|
||||||
|
unsigned short data;
|
||||||
|
unsigned char mux_sel;
|
||||||
|
struct da9055 *da9055 = hwmon->da9055;
|
||||||
|
|
||||||
|
if (channel > DA9055_ADC_TJUNC)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&hwmon->irq_lock);
|
||||||
|
|
||||||
|
/* Selects desired MUX for manual conversion */
|
||||||
|
mux_sel = chan_mux[channel] | DA9055_ADC_MAN_CONV;
|
||||||
|
|
||||||
|
ret = da9055_reg_write(da9055, DA9055_REG_ADC_MAN, mux_sel);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* Wait for an interrupt */
|
||||||
|
if (!wait_for_completion_timeout(&hwmon->done,
|
||||||
|
msecs_to_jiffies(500))) {
|
||||||
|
dev_err(da9055->dev,
|
||||||
|
"timeout waiting for ADC conversion interrupt\n");
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_H);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
calc_data = (unsigned short)ret;
|
||||||
|
data = calc_data << 2;
|
||||||
|
|
||||||
|
ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_L);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
calc_data = (unsigned short)(ret & DA9055_ADC_LSB_MASK);
|
||||||
|
data |= calc_data;
|
||||||
|
|
||||||
|
ret = data;
|
||||||
|
|
||||||
|
err:
|
||||||
|
mutex_unlock(&hwmon->irq_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data)
|
||||||
|
{
|
||||||
|
struct da9055_hwmon *hwmon = irq_data;
|
||||||
|
|
||||||
|
complete(&hwmon->done);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Conversion function for VSYS and ADCINx */
|
||||||
|
static inline int volt_reg_to_mV(int value, int channel)
|
||||||
|
{
|
||||||
|
if (channel == DA9055_ADC_VSYS)
|
||||||
|
return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500;
|
||||||
|
else
|
||||||
|
return DIV_ROUND_CLOSEST(value * 1000, DA9055_ADCIN_DIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9055_enable_auto_mode(struct da9055 *da9055, int channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel,
|
||||||
|
1 << channel);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9055_disable_auto_mode(struct da9055 *da9055, int channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t da9055_read_auto_ch(struct device *dev,
|
||||||
|
struct device_attribute *devattr, char *buf)
|
||||||
|
{
|
||||||
|
struct da9055_hwmon *hwmon = dev_get_drvdata(dev);
|
||||||
|
int ret, adc;
|
||||||
|
int channel = to_sensor_dev_attr(devattr)->index;
|
||||||
|
|
||||||
|
mutex_lock(&hwmon->hwmon_lock);
|
||||||
|
|
||||||
|
ret = da9055_enable_auto_mode(hwmon->da9055, channel);
|
||||||
|
if (ret < 0)
|
||||||
|
goto hwmon_err;
|
||||||
|
|
||||||
|
usleep_range(10000, 10500);
|
||||||
|
|
||||||
|
adc = da9055_reg_read(hwmon->da9055, DA9055_REG_VSYS_RES + channel);
|
||||||
|
if (adc < 0) {
|
||||||
|
ret = adc;
|
||||||
|
goto hwmon_err_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = da9055_disable_auto_mode(hwmon->da9055, channel);
|
||||||
|
if (ret < 0)
|
||||||
|
goto hwmon_err;
|
||||||
|
|
||||||
|
mutex_unlock(&hwmon->hwmon_lock);
|
||||||
|
|
||||||
|
return sprintf(buf, "%d\n", volt_reg_to_mV(adc, channel));
|
||||||
|
|
||||||
|
hwmon_err_release:
|
||||||
|
da9055_disable_auto_mode(hwmon->da9055, channel);
|
||||||
|
hwmon_err:
|
||||||
|
mutex_unlock(&hwmon->hwmon_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t da9055_read_tjunc(struct device *dev,
|
||||||
|
struct device_attribute *devattr, char *buf)
|
||||||
|
{
|
||||||
|
struct da9055_hwmon *hwmon = dev_get_drvdata(dev);
|
||||||
|
int tjunc;
|
||||||
|
int toffset;
|
||||||
|
|
||||||
|
tjunc = da9055_adc_manual_read(hwmon, DA9055_ADC_TJUNC);
|
||||||
|
if (tjunc < 0)
|
||||||
|
return tjunc;
|
||||||
|
|
||||||
|
toffset = da9055_reg_read(hwmon->da9055, DA9055_REG_T_OFFSET);
|
||||||
|
if (toffset < 0)
|
||||||
|
return toffset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332
|
||||||
|
* T_OFFSET is a trim value used to improve accuracy of the result
|
||||||
|
*/
|
||||||
|
return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc - toffset)
|
||||||
|
+ 3076332, 10000));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t da9055_hwmon_show_name(struct device *dev,
|
||||||
|
struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
return sprintf(buf, "da9055-hwmon\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t show_label(struct device *dev,
|
||||||
|
struct device_attribute *devattr, char *buf)
|
||||||
|
{
|
||||||
|
return sprintf(buf, "%s\n",
|
||||||
|
input_names[to_sensor_dev_attr(devattr)->index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9055_read_auto_ch, NULL,
|
||||||
|
DA9055_ADC_VSYS);
|
||||||
|
static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL,
|
||||||
|
DA9055_ADC_VSYS);
|
||||||
|
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, da9055_read_auto_ch, NULL,
|
||||||
|
DA9055_ADC_ADCIN1);
|
||||||
|
static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_label, NULL,
|
||||||
|
DA9055_ADC_ADCIN1);
|
||||||
|
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, da9055_read_auto_ch, NULL,
|
||||||
|
DA9055_ADC_ADCIN2);
|
||||||
|
static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_label, NULL,
|
||||||
|
DA9055_ADC_ADCIN2);
|
||||||
|
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9055_read_auto_ch, NULL,
|
||||||
|
DA9055_ADC_ADCIN3);
|
||||||
|
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL,
|
||||||
|
DA9055_ADC_ADCIN3);
|
||||||
|
|
||||||
|
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, da9055_read_tjunc, NULL,
|
||||||
|
DA9055_ADC_TJUNC);
|
||||||
|
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL,
|
||||||
|
DA9055_ADC_TJUNC);
|
||||||
|
|
||||||
|
static DEVICE_ATTR(name, S_IRUGO, da9055_hwmon_show_name, NULL);
|
||||||
|
|
||||||
|
static struct attribute *da9055_attr[] = {
|
||||||
|
&dev_attr_name.attr,
|
||||||
|
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in0_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in1_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in2_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in3_label.dev_attr.attr,
|
||||||
|
|
||||||
|
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group da9055_attr_group = {.attrs = da9055_attr};
|
||||||
|
|
||||||
|
static int da9055_hwmon_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct da9055_hwmon *hwmon;
|
||||||
|
int hwmon_irq, ret;
|
||||||
|
|
||||||
|
hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9055_hwmon),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!hwmon)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&hwmon->hwmon_lock);
|
||||||
|
mutex_init(&hwmon->irq_lock);
|
||||||
|
|
||||||
|
init_completion(&hwmon->done);
|
||||||
|
hwmon->da9055 = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, hwmon);
|
||||||
|
|
||||||
|
hwmon_irq = platform_get_irq_byname(pdev, "HWMON");
|
||||||
|
if (hwmon_irq < 0)
|
||||||
|
return hwmon_irq;
|
||||||
|
|
||||||
|
hwmon_irq = regmap_irq_get_virq(hwmon->da9055->irq_data, hwmon_irq);
|
||||||
|
if (hwmon_irq < 0)
|
||||||
|
return hwmon_irq;
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(&pdev->dev, hwmon_irq,
|
||||||
|
NULL, da9055_auxadc_irq,
|
||||||
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||||
|
"adc-irq", hwmon);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sysfs_create_group(&pdev->dev.kobj, &da9055_attr_group);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
hwmon->class_device = hwmon_device_register(&pdev->dev);
|
||||||
|
if (IS_ERR(hwmon->class_device)) {
|
||||||
|
ret = PTR_ERR(hwmon->class_device);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9055_hwmon_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct da9055_hwmon *hwmon = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group);
|
||||||
|
hwmon_device_unregister(hwmon->class_device);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver da9055_hwmon_driver = {
|
||||||
|
.probe = da9055_hwmon_probe,
|
||||||
|
.remove = da9055_hwmon_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "da9055-hwmon",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(da9055_hwmon_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
|
||||||
|
MODULE_DESCRIPTION("DA9055 HWMON driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:da9055-hwmon");
|
@@ -31,6 +31,9 @@ MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
|
|||||||
MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
|
MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
/* Family 16h Northbridge's function 4 PCI ID */
|
||||||
|
#define PCI_DEVICE_ID_AMD_16H_NB_F4 0x1534
|
||||||
|
|
||||||
/* D18F3 */
|
/* D18F3 */
|
||||||
#define REG_NORTHBRIDGE_CAP 0xe8
|
#define REG_NORTHBRIDGE_CAP 0xe8
|
||||||
|
|
||||||
@@ -248,6 +251,7 @@ static void __devexit fam15h_power_remove(struct pci_dev *pdev)
|
|||||||
|
|
||||||
static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
|
static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
|
||||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
|
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
|
||||||
|
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
|
MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
|
||||||
|
@@ -302,19 +302,8 @@ static struct i2c_driver ina2xx_driver = {
|
|||||||
.id_table = ina2xx_id,
|
.id_table = ina2xx_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init ina2xx_init(void)
|
module_i2c_driver(ina2xx_driver);
|
||||||
{
|
|
||||||
return i2c_add_driver(&ina2xx_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit ina2xx_exit(void)
|
|
||||||
{
|
|
||||||
i2c_del_driver(&ina2xx_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>");
|
MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>");
|
||||||
MODULE_DESCRIPTION("ina2xx driver");
|
MODULE_DESCRIPTION("ina2xx driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
module_init(ina2xx_init);
|
|
||||||
module_exit(ina2xx_exit);
|
|
||||||
|
29
include/linux/platform_data/ads7828.h
Normal file
29
include/linux/platform_data/ads7828.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* TI ADS7828 A/D Converter platform data definition
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 Savoir-faire Linux Inc.
|
||||||
|
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
*
|
||||||
|
* For further information, see the Documentation/hwmon/ads7828 file.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PDATA_ADS7828_H
|
||||||
|
#define _PDATA_ADS7828_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ads7828_platform_data - optional ADS7828 connectivity info
|
||||||
|
* @diff_input: Differential input mode.
|
||||||
|
* @ext_vref: Use an external voltage reference.
|
||||||
|
* @vref_mv: Voltage reference value, if external.
|
||||||
|
*/
|
||||||
|
struct ads7828_platform_data {
|
||||||
|
bool diff_input;
|
||||||
|
bool ext_vref;
|
||||||
|
unsigned int vref_mv;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _PDATA_ADS7828_H */
|
Reference in New Issue
Block a user