Commit a8936db7 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 11b84c58 44f751ce
......@@ -4,29 +4,47 @@ Kernel driver ads7828
Supported chips:
* Texas Instruments/Burr-Brown 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
* Texas Instruments ADS7830
Prefix: 'ads7830'
Datasheet: Publicly available at the Texas Instruments website:
http://focus.ti.com/lit/ds/symlink/ads7830.pdf
Authors:
Steve Hardy <shardy@redhat.com>
Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
Platform data
-------------
The ads7828 driver accepts an optional ads7828_platform_data structure (defined
in include/linux/platform_data/ads7828.h). The structure fields are:
Module Parameters
-----------------
* diff_input: (bool) Differential operation
set to true for differential mode, false for default single ended mode.
* se_input: bool (default Y)
Single ended operation - set to N for differential mode
* int_vref: bool (default Y)
Operate with the internal 2.5V reference - set to N for external reference
* vref_mv: int (default 2500)
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
-----------
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,
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
may be required if your hardware supplies the ADS7828 from a 5V supply, see
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
D525/510/425/410 100
Z670/650 90
Z560/550/540/530P/530/520PT/520/515/510PT/510P 90
Z510/500 90
N570/550 100
N475/470/455/450 100
N280/270 90
330/230 125
......
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
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
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
depends on PCI
......@@ -455,7 +465,7 @@ config SENSORS_HIH6130
config SENSORS_CORETEMP
tristate "Intel Core/Core2/Atom temperature sensor"
depends on X86 && PCI
depends on X86
help
If you say yes here you get support for the temperature
sensor inside your CPU. Most of the family 6 CPUs
......@@ -1106,11 +1116,12 @@ config SENSORS_ADS1015
will be called ads1015.
config SENSORS_ADS7828
tristate "Texas Instruments ADS7828"
tristate "Texas Instruments ADS7828 and compatibles"
depends on I2C
help
If you say yes here you get support for Texas Instruments ADS7828
12-bit 8-channel ADC device.
If you say yes here you get support for Texas Instruments ADS7828 and
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
will be called ads7828.
......
......@@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.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_DS620) += ds620.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
......
/*
* 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
*
* This driver is based on the lm75 and other lm_sensors/hwmon drivers
*
* 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
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/hwmon.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/platform_data/ads7828.h>
#include <linux/slab.h>
/* The ADS7828 registers */
#define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */
#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
#define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */
#define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */
#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 */
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
/* Module parameters */
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 */
#define ADS7828_NCH 8 /* 8 channels supported */
#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */
#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */
#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 */
/* List of supported devices */
enum ads7828_chips { ads7828, ads7830 };
/* Client specific data */
struct ads7828_data {
struct device *hwmon_dev;
struct mutex update_lock; /* mutex protect updates */
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */
struct mutex update_lock; /* Mutex protecting updates */
unsigned long last_updated; /* Last updated time (in jiffies) */
u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 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 */
static int ads7828_detect(struct i2c_client *client,
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)
/* Command byte C2,C1,C0 - see datasheet */
static inline u8 ads7828_cmd_byte(u8 cmd, int ch)
{
/* cmd byte C2,C1,C0 - see datasheet */
u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4);
cmd |= ads7828_cmd_byte;
return cmd;
return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4);
}
/* 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");
for (ch = 0; ch < ADS7828_NCH; ch++) {
u8 cmd = channel_cmd_byte(ch);
data->adc_input[ch] =
i2c_smbus_read_word_swapped(client, cmd);
u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch);
data->adc_input[ch] = data->read_channel(client, cmd);
}
data->last_updated = jiffies;
data->valid = 1;
data->valid = true;
}
mutex_unlock(&data->update_lock);
......@@ -110,28 +96,25 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
}
/* sysfs callback function */
static ssize_t show_in(struct device *dev, struct device_attribute *da,
char *buf)
static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ads7828_data *data = ads7828_update_device(dev);
/* Print value (in mV as specified in sysfs-interface documentation) */
return sprintf(buf, "%d\n", (data->adc_input[attr->index] *
ads7828_lsb_resol)/1000);
}
unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] *
data->lsb_resol, 1000);
#define in_reg(offset)\
static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\
NULL, offset)
return sprintf(buf, "%d\n", value);
}
in_reg(0);
in_reg(1);
in_reg(2);
in_reg(3);
in_reg(4);
in_reg(5);
in_reg(6);
in_reg(7);
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1);
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);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4);
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7);
static struct attribute *ads7828_attributes[] = {
&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)
{
struct ads7828_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
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;
}
......@@ -213,6 +145,7 @@ static int ads7828_detect(struct i2c_client *client,
static int ads7828_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ads7828_platform_data *pdata = client->dev.platform_data;
struct ads7828_data *data;
int err;
......@@ -221,10 +154,37 @@ static int ads7828_probe(struct i2c_client *client,
if (!data)
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);
mutex_init(&data->update_lock);
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
if (err)
return err;
......@@ -232,38 +192,35 @@ static int ads7828_probe(struct i2c_client *client,
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove;
goto error;
}
return 0;
exit_remove:
error:
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
return err;
}
static int __init sensors_ads7828_init(void)
{
/* Initialize the command byte according to module parameters */
ads7828_cmd_byte = se_input ?
ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF;
ads7828_cmd_byte |= int_vref ?
ADS7828_CMD_PD3 : ADS7828_CMD_PD1;
static const struct i2c_device_id ads7828_device_ids[] = {
{ "ads7828", ads7828 },
{ "ads7830", ads7830 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
/* Calculate the LSB resolution */
ads7828_lsb_resol = (vref_mv*1000)/4096;
static struct i2c_driver ads7828_driver = {
.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)
{
i2c_del_driver(&ads7828_driver);
}
module_i2c_driver(ads7828_driver);
MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
MODULE_DESCRIPTION("ADS7828 driver");
MODULE_LICENSE("GPL");
module_init(sensors_ads7828_init);
module_exit(sensors_ads7828_exit);
MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles");
......@@ -34,7 +34,6 @@
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/cpu.h>
#include <linux/pci.h>
#include <linux/smp.h>
#include <linux/moduleparam.h>
#include <asm/msr.h>
......@@ -197,14 +196,6 @@ struct tjmax {
};
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 330", 125000 }, /* Model 0x1c, stepping 2 */
{ "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 */
};
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,
struct device *dev)
{
......@@ -222,7 +235,6 @@ static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id,
int usemsr_ee = 1;
int err;
u32 eax, edx;
struct pci_dev *host_bridge;
int i;
/* 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;
}
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 */
if (c->x86_model == 0xf && c->x86_mask < 4)
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) {
u8 platform_id;
......
......@@ -60,30 +60,17 @@ static inline int vbbat_reg_to_mV(int value)
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;
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);
return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
DA9052_ADCCONT_AUTOVDDEN,
DA9052_ADCCONT_AUTOVDDEN);
}
static int da9052_disable_vddout_channel(struct da9052 *da9052)
static inline int da9052_disable_vddout_channel(struct da9052 *da9052)
{
int ret;
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);
return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
DA9052_ADCCONT_AUTOVDDEN, 0);
}
static ssize_t da9052_read_vddout(struct device *dev,
......
/*
* 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_LICENSE("GPL");
/* Family 16h Northbridge's function 4 PCI ID */
#define PCI_DEVICE_ID_AMD_16H_NB_F4 0x1534
/* D18F3 */
#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) = {
{ 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);
......
......@@ -302,19 +302,8 @@ static struct i2c_driver ina2xx_driver = {
.id_table = ina2xx_id,
};
static int __init ina2xx_init(void)
{
return i2c_add_driver(&ina2xx_driver);
}
static void __exit ina2xx_exit(void)
{
i2c_del_driver(&ina2xx_driver);
}
module_i2c_driver(ina2xx_driver);
MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>");
MODULE_DESCRIPTION("ina2xx driver");
MODULE_LICENSE("GPL");
module_init(ina2xx_init);
module_exit(ina2xx_exit);
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment