Commit 2634bf25 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-linus-v4.5' of...

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

Pull hwmon updates from Guenter Roeck:
 "Notable hwmon changes:

   - Add basic support for NCT6683 on Mitac boards
   - Add support for AMD new 15h processors to fam15h_power driver
   - Add pmbus client driver for LTC3815
   - Remove htu21 driver (now supported by iio subsystem)"

* tag 'hwmon-for-linus-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: (nct6683) Add basic support for NCT6683 on Mitac boards
  hwmon: (ibmaem) constify aem_rw_sensor_template and aem_ro_sensor_template structures
  hwmon: (nct6683,nct6775) constify sensor_template_group structures
  MAINTAINERS: change the maintainer of fam15h_power driver
  hwmon: (fam15h_power) Add support for AMD new 15h processors
  hwmon: (pmbus) Add client driver for LTC3815
  hwmon: (htu21) Remove driver
parents e795e5f4 91918d13
......@@ -1507,6 +1507,14 @@ S: 312/107 Canberra Avenue
S: Griffith, ACT 2603
S: Australia
N: Andreas Herrmann
E: herrmann.der.user@gmail.com
E: herrmann.der.user@googlemail.com
D: Key developer of x86/AMD64
D: Author of AMD family 15h processor power monitoring driver
D: Maintainer of AMD Athlon 64 and Opteron processor frequency driver
S: Germany
N: Sebastian Hetze
E: she@lunetix.de
D: German Linux Documentation,
......
Kernel driver htu21
===================
Supported chips:
* Measurement Specialties HTU21D
Prefix: 'htu21'
Addresses scanned: none
Datasheet: Publicly available at the Measurement Specialties website
http://www.meas-spec.com/downloads/HTU21D.pdf
Author:
William Markezana <william.markezana@meas-spec.com>
Description
-----------
The HTU21D is a humidity and temperature sensor in a DFN package of
only 3 x 3 mm footprint and 0.9 mm height.
The devices communicate with the I2C protocol. All sensors are set to the
same I2C address 0x40, so an entry with I2C_BOARD_INFO("htu21", 0x40) can
be used in the board setup code.
This driver does not auto-detect devices. You will have to instantiate the
devices explicitly. Please see Documentation/i2c/instantiating-devices
for details.
sysfs-Interface
---------------
temp1_input - temperature input
humidity1_input - humidity input
Notes
-----
The driver uses the default resolution settings of 12 bit for humidity and 14
bit for temperature, which results in typical measurement times of 11 ms for
humidity and 44 ms for temperature. To keep self heating below 0.1 degree
Celsius, the device should not be active for more than 10% of the time. For
this reason, the driver performs no more than two measurements per second and
reports cached information if polled more frequently.
Different resolutions, the on-chip heater, using the CRC checksum and reading
the serial number are not supported yet.
Kernel driver ltc3815
=====================
Supported chips:
* Linear Technology LTC3815
Prefix: 'ltc3815'
Addresses scanned: -
Datasheet: http://www.linear.com/product/ltc3815
Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
LTC3815 is a Monolithic Synchronous DC/DC Step-Down Converter.
Usage Notes
-----------
This driver does not probe for PMBus devices. You will have to instantiate
devices explicitly.
Example: the following commands will load the driver for an LTC3815
at address 0x20 on I2C bus #1:
# modprobe ltc3815
# echo ltc3815 0x20 > /sys/bus/i2c/devices/i2c-1/new_device
Sysfs attributes
----------------
in1_label "vin"
in1_input Measured input voltage.
in1_alarm Input voltage alarm.
in1_highest Highest input voltage.
in1_reset_history Reset input voltage history.
in2_label "vout1".
in2_input Measured output voltage.
in2_alarm Output voltage alarm.
in2_highest Highest output voltage.
in2_reset_history Reset output voltage history.
temp1_input Measured chip temperature.
temp1_alarm Temperature alarm.
temp1_highest Highest measured temperature.
temp1_reset_history Reset temperature history.
curr1_label "iin".
curr1_input Measured input current.
curr1_highest Highest input current.
curr1_reset_history Reset input current history.
curr2_label "iout1".
curr2_input Measured output current.
curr2_alarm Output current alarm.
curr2_highest Highest output current.
curr2_reset_history Reset output current history.
......@@ -614,9 +614,9 @@ F: drivers/crypto/ccp/
F: include/linux/ccp.h
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
M: Andreas Herrmann <herrmann.der.user@googlemail.com>
M: Huang Rui <ray.huang@amd.com>
L: lm-sensors@lm-sensors.org
S: Maintained
S: Supported
F: Documentation/hwmon/fam15h_power
F: drivers/hwmon/fam15h_power.c
......
......@@ -859,16 +859,6 @@ config SENSORS_MAX31790
This driver can also be built as a module. If so, the module
will be called max31790.
config SENSORS_HTU21
tristate "Measurement Specialties HTU21D humidity/temperature sensors"
depends on I2C
help
If you say yes here you get support for the Measurement Specialties
HTU21D humidity and temperature sensors.
This driver can also be built as a module. If so, the module
will be called htu21.
config SENSORS_MCP3021
tristate "Microchip MCP3021 and compatibles"
depends on I2C
......
......@@ -68,7 +68,6 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
obj-$(CONFIG_SENSORS_HTU21) += htu21.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
......
......@@ -47,6 +47,8 @@ MODULE_LICENSE("GPL");
#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4
struct fam15h_power_data {
struct pci_dev *pdev;
unsigned int tdp_to_watts;
......@@ -124,7 +126,7 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev,
if (c->x86 == 0x15 &&
(c->x86_model <= 0xf ||
(c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
(c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
n += 1;
fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
......@@ -138,7 +140,7 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev,
fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
if (c->x86 == 0x15 &&
(c->x86_model <= 0xf ||
(c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
(c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
data->group.attrs = fam15h_power_attrs;
......@@ -296,6 +298,7 @@ static const struct pci_device_id fam15h_power_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
{}
......
/*
* Measurement Specialties HTU21D humidity and temperature sensor driver
*
* Copyright (C) 2013 William Markezana <william.markezana@meas-spec.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/jiffies.h>
/* HTU21 Commands */
#define HTU21_T_MEASUREMENT_HM 0xE3
#define HTU21_RH_MEASUREMENT_HM 0xE5
struct htu21 {
struct i2c_client *client;
struct mutex lock;
bool valid;
unsigned long last_update;
int temperature;
int humidity;
};
static inline int htu21_temp_ticks_to_millicelsius(int ticks)
{
ticks &= ~0x0003; /* clear status bits */
/*
* Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14,
* optimized for integer fixed point (3 digits) arithmetic
*/
return ((21965 * ticks) >> 13) - 46850;
}
static inline int htu21_rh_ticks_to_per_cent_mille(int ticks)
{
ticks &= ~0x0003; /* clear status bits */
/*
* Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14,
* optimized for integer fixed point (3 digits) arithmetic
*/
return ((15625 * ticks) >> 13) - 6000;
}
static int htu21_update_measurements(struct device *dev)
{
struct htu21 *htu21 = dev_get_drvdata(dev);
struct i2c_client *client = htu21->client;
int ret = 0;
mutex_lock(&htu21->lock);
if (time_after(jiffies, htu21->last_update + HZ / 2) ||
!htu21->valid) {
ret = i2c_smbus_read_word_swapped(client,
HTU21_T_MEASUREMENT_HM);
if (ret < 0)
goto out;
htu21->temperature = htu21_temp_ticks_to_millicelsius(ret);
ret = i2c_smbus_read_word_swapped(client,
HTU21_RH_MEASUREMENT_HM);
if (ret < 0)
goto out;
htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret);
htu21->last_update = jiffies;
htu21->valid = true;
}
out:
mutex_unlock(&htu21->lock);
return ret >= 0 ? 0 : ret;
}
static ssize_t htu21_show_temperature(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct htu21 *htu21 = dev_get_drvdata(dev);
int ret;
ret = htu21_update_measurements(dev);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", htu21->temperature);
}
static ssize_t htu21_show_humidity(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct htu21 *htu21 = dev_get_drvdata(dev);
int ret;
ret = htu21_update_measurements(dev);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", htu21->humidity);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
htu21_show_temperature, NULL, 0);
static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
htu21_show_humidity, NULL, 0);
static struct attribute *htu21_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_humidity1_input.dev_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(htu21);
static int htu21_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct htu21 *htu21;
struct device *hwmon_dev;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
dev_err(&client->dev,
"adapter does not support SMBus word transactions\n");
return -ENODEV;
}
htu21 = devm_kzalloc(dev, sizeof(*htu21), GFP_KERNEL);
if (!htu21)
return -ENOMEM;
htu21->client = client;
mutex_init(&htu21->lock);
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
htu21,
htu21_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id htu21_id[] = {
{ "htu21", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, htu21_id);
static struct i2c_driver htu21_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "htu21",
},
.probe = htu21_probe,
.id_table = htu21_id,
};
module_i2c_driver(htu21_driver);
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver");
MODULE_LICENSE("GPL");
......@@ -920,8 +920,8 @@ static ssize_t aem_set_power_period(struct device *dev,
/* Discover sensors on an AEM device */
static int aem_register_sensors(struct aem_data *data,
struct aem_ro_sensor_template *ro,
struct aem_rw_sensor_template *rw)
const struct aem_ro_sensor_template *ro,
const struct aem_rw_sensor_template *rw)
{
struct device *dev = &data->pdev->dev;
struct sensor_device_attribute *sensors = data->sensors;
......@@ -1020,19 +1020,19 @@ static void aem_remove_sensors(struct aem_data *data)
/* Sensor probe functions */
/* Description of AEM1 sensors */
static struct aem_ro_sensor_template aem1_ro_sensors[] = {
static const struct aem_ro_sensor_template aem1_ro_sensors[] = {
{"energy1_input", aem_show_energy, 0},
{"power1_average", aem_show_power, 0},
{NULL, NULL, 0},
};
static struct aem_rw_sensor_template aem1_rw_sensors[] = {
static const struct aem_rw_sensor_template aem1_rw_sensors[] = {
{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
{NULL, NULL, NULL, 0},
};
/* Description of AEM2 sensors */
static struct aem_ro_sensor_template aem2_ro_sensors[] = {
static const struct aem_ro_sensor_template aem2_ro_sensors[] = {
{"energy1_input", aem_show_energy, 0},
{"energy2_input", aem_show_energy, 1},
{"power1_average", aem_show_power, 0},
......@@ -1050,7 +1050,7 @@ static struct aem_ro_sensor_template aem2_ro_sensors[] = {
{NULL, NULL, 0},
};
static struct aem_rw_sensor_template aem2_rw_sensors[] = {
static const struct aem_rw_sensor_template aem2_rw_sensors[] = {
{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
{"power2_average_interval", aem_show_power_period, aem_set_power_period, 1},
{NULL, NULL, NULL, 0},
......
......@@ -29,7 +29,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
......@@ -45,7 +45,7 @@ enum kinds { nct6683 };
static bool force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Set to one to enable detection on non-Intel boards");
MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors");
static const char * const nct6683_device_names[] = {
"nct6683",
......@@ -141,6 +141,7 @@ superio_exit(int ioreg)
#define NCT6683_REG_MON(x) (0x100 + (x) * 2)
#define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2)
#define NCT6683_REG_PWM(x) (0x160 + (x))
#define NCT6683_REG_PWM_WRITE(x) (0xa28 + (x))
#define NCT6683_REG_MON_STS(x) (0x174 + (x))
#define NCT6683_REG_IDLE(x) (0x178 + (x))
......@@ -165,8 +166,13 @@ superio_exit(int ioreg)
#define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */
#define NCT6683_REG_FAN_CFG_CTRL 0xa01
#define NCT6683_FAN_CFG_REQ 0x80
#define NCT6683_FAN_CFG_DONE 0x40
#define NCT6683_REG_CUSTOMER_ID 0x602
#define NCT6683_CUSTOMER_ID_INTEL 0x805
#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
#define NCT6683_REG_BUILD_YEAR 0x604
#define NCT6683_REG_BUILD_MONTH 0x605
......@@ -394,7 +400,8 @@ struct sensor_template_group {
};
static struct attribute_group *
nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg,
nct6683_create_attr_group(struct device *dev,
const struct sensor_template_group *tg,
int repeat)
{
struct sensor_device_attribute_2 *a2;
......@@ -559,6 +566,7 @@ static int get_temp_reg(struct nct6683_data *data, int nr, int index)
break;
}
break;
case NCT6683_CUSTOMER_ID_MITAC:
default:
switch (nr) {
default:
......@@ -703,7 +711,7 @@ static struct sensor_device_template *nct6683_attributes_in_template[] = {
NULL
};
static struct sensor_template_group nct6683_in_template_group = {
static const struct sensor_template_group nct6683_in_template_group = {
.templates = nct6683_attributes_in_template,
.is_visible = nct6683_in_is_visible,
};
......@@ -774,7 +782,7 @@ static struct sensor_device_template *nct6683_attributes_fan_template[] = {
NULL
};
static struct sensor_template_group nct6683_fan_template_group = {
static const struct sensor_template_group nct6683_fan_template_group = {
.templates = nct6683_attributes_fan_template,
.is_visible = nct6683_fan_is_visible,
.base = 1,
......@@ -902,7 +910,7 @@ static struct sensor_device_template *nct6683_attributes_temp_template[] = {
NULL
};
static struct sensor_template_group nct6683_temp_template_group = {
static const struct sensor_template_group nct6683_temp_template_group = {
.templates = nct6683_attributes_temp_template,
.is_visible = nct6683_temp_is_visible,
.base = 1,
......@@ -918,7 +926,29 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%d\n", data->pwm[index]);
}
SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, NULL, 0);
static ssize_t
store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct nct6683_data *data = dev_get_drvdata(dev);
int index = sattr->index;
unsigned long val;
if (kstrtoul(buf, 10, &val) || val > 255)
return -EINVAL;
mutex_lock(&data->update_lock);
nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ);
usleep_range(1000, 2000);
nct6683_write(data, NCT6683_REG_PWM_WRITE(index), val);
nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE);
mutex_unlock(&data->update_lock);
return count;
}
SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, store_pwm, 0);
static umode_t nct6683_pwm_is_visible(struct kobject *kobj,
struct attribute *attr, int index)
......@@ -930,6 +960,10 @@ static umode_t nct6683_pwm_is_visible(struct kobject *kobj,
if (!(data->have_pwm & (1 << pwm)))
return 0;
/* Only update pwm values for Mitac boards */
if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC)
return attr->mode | S_IWUSR;
return attr->mode;
}
......@@ -938,7 +972,7 @@ static struct sensor_device_template *nct6683_attributes_pwm_template[] = {
NULL
};
static struct sensor_template_group nct6683_pwm_template_group = {
static const struct sensor_template_group nct6683_pwm_template_group = {
.templates = nct6683_attributes_pwm_template,
.is_visible = nct6683_pwm_is_visible,
.base = 1,
......@@ -1170,6 +1204,7 @@ static int nct6683_probe(struct platform_device *pdev)
struct device *hwmon_dev;
struct resource *res;
int groups = 0;
char build[16];
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
......@@ -1187,6 +1222,17 @@ static int nct6683_probe(struct platform_device *pdev)
data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID);
/* By default only instantiate driver if the customer ID is known */
switch (data->customer_id) {
case NCT6683_CUSTOMER_ID_INTEL:
break;
case NCT6683_CUSTOMER_ID_MITAC:
break;
default:
if (!force)
return -ENODEV;
}
nct6683_init_device(data);
nct6683_setup_fans(data);
nct6683_setup_sensors(data);
......@@ -1230,14 +1276,23 @@ static int nct6683_probe(struct platform_device *pdev)
}
data->groups[groups++] = &nct6683_group_other;
dev_info(dev, "%s EC firmware version %d.%d build %02x/%02x/%02x\n",
nct6683_chip_names[data->kind],
nct6683_read(data, NCT6683_REG_VERSION_HI),
nct6683_read(data, NCT6683_REG_VERSION_LO),
if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
scnprintf(build, sizeof(build), "%02x/%02x/%02x",
nct6683_read(data, NCT6683_REG_BUILD_MONTH),
nct6683_read(data, NCT6683_REG_BUILD_DAY),
nct6683_read(data, NCT6683_REG_BUILD_YEAR));
else
scnprintf(build, sizeof(build), "%02d/%02d/%02d",
nct6683_read(data, NCT6683_REG_BUILD_MONTH),
nct6683_read(data, NCT6683_REG_BUILD_DAY),
nct6683_read(data, NCT6683_REG_BUILD_YEAR));
dev_info(dev, "%s EC firmware version %d.%d build %s\n",
nct6683_chip_names[data->kind],
nct6683_read(data, NCT6683_REG_VERSION_HI),
nct6683_read(data, NCT6683_REG_VERSION_LO),
build);
hwmon_dev = devm_hwmon_device_register_with_groups(dev,
nct6683_device_names[data->kind], data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
......@@ -1292,20 +1347,10 @@ static struct platform_driver nct6683_driver = {
static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
{
const char *board_vendor;
int addr;
u16 val;
int err;
/*
* Only run on Intel boards unless the 'force' module parameter is set
*/
if (!force) {
board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
if (!board_vendor || strcmp(board_vendor, "Intel Corporation"))
return -ENODEV;
}
err = superio_enter(sioaddr);
if (err)
return err;
......
......@@ -1045,7 +1045,8 @@ struct sensor_template_group {
};
static struct attribute_group *
nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
nct6775_create_attr_group(struct device *dev,
const struct sensor_template_group *tg,
int repeat)
{
struct attribute_group *group;
......@@ -1827,7 +1828,7 @@ static struct sensor_device_template *nct6775_attributes_in_template[] = {
NULL
};
static struct sensor_template_group nct6775_in_template_group = {
static const struct sensor_template_group nct6775_in_template_group = {
.templates = nct6775_attributes_in_template,
.is_visible = nct6775_in_is_visible,
};
......@@ -2046,7 +2047,7 @@ static struct sensor_device_template *nct6775_attributes_fan_template[] = {
NULL
};
static struct sensor_template_group nct6775_fan_template_group = {
static const struct sensor_template_group nct6775_fan_template_group = {
.templates = nct6775_attributes_fan_template,
.is_visible = nct6775_fan_is_visible,
.base = 1,
......@@ -2255,7 +2256,7 @@ static struct sensor_device_template *nct6775_attributes_temp_template[] = {
NULL
};
static struct sensor_template_group nct6775_temp_template_group = {
static const struct sensor_template_group nct6775_temp_template_group = {
.templates = nct6775_attributes_temp_template,
.is_visible = nct6775_temp_is_visible,
.base = 1,
......@@ -3117,7 +3118,7 @@ static struct sensor_device_template *nct6775_attributes_pwm_template[] = {
NULL
};
static struct sensor_template_group nct6775_pwm_template_group = {
static const struct sensor_template_group nct6775_pwm_template_group = {
.templates = nct6775_attributes_pwm_template,
.is_visible = nct6775_pwm_is_visible,
.base = 1,
......
......@@ -65,6 +65,16 @@ config SENSORS_LTC2978_REGULATOR
If you say yes here you get regulator support for Linear
Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
config SENSORS_LTC3815
tristate "Linear Technologies LTC3815"
default n
help
If you say yes here you get hardware monitoring support for Linear
Technology LTC3815.
This driver can also be built as a module. If so, the module will
be called ltc3815.
config SENSORS_MAX16064
tristate "Maxim MAX16064"
default n
......
......@@ -7,6 +7,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
......
/*
* Hardware monitoring driver for LTC3815
*
* Copyright (c) 2015 Linear Technology
* Copyright (c) 2015 Guenter Roeck
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"
#define LTC3815_MFR_IOUT_PEAK 0xd7
#define LTC3815_MFR_VOUT_PEAK 0xdd
#define LTC3815_MFR_VIN_PEAK 0xde
#define LTC3815_MFR_TEMP_PEAK 0xdf
#define LTC3815_MFR_IIN_PEAK 0xe1
#define LTC3815_MFR_SPECIAL_ID 0xe7
#define LTC3815_ID 0x8000
#define LTC3815_ID_MASK 0xff00
static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg)
{
int ret;
switch (reg) {
case PMBUS_VOUT_MODE:
/*
* The chip returns 0x3e, suggesting VID mode with manufacturer
* specific VID codes. Since the output voltage is reported
* with a LSB of 0.5mV, override and report direct mode with
* appropriate coefficients.
*/
ret = 0x40;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg)
{
int ret;
switch (reg) {
case PMBUS_CLEAR_FAULTS:
/*
* LTC3815 does not support the CLEAR_FAULTS command.
* Emulate it by clearing the status register.
*/
ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD);
if (ret > 0) {
pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD,
ret);
ret = 0;
}
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg)
{
int ret;
switch (reg) {
case PMBUS_VIRT_READ_VIN_MAX:
ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK);
break;
case PMBUS_VIRT_READ_VOUT_MAX:
ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK);
break;
case PMBUS_VIRT_READ_TEMP_MAX:
ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK);
break;
case PMBUS_VIRT_READ_IOUT_MAX:
ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK);
break;
case PMBUS_VIRT_READ_IIN_MAX:
ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK);
break;
case PMBUS_VIRT_RESET_VOUT_HISTORY:
case PMBUS_VIRT_RESET_VIN_HISTORY:
case PMBUS_VIRT_RESET_TEMP_HISTORY:
case PMBUS_VIRT_RESET_IOUT_HISTORY:
case PMBUS_VIRT_RESET_IIN_HISTORY:
ret = 0;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ltc3815_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
int ret;
switch (reg) {
case PMBUS_VIRT_RESET_IIN_HISTORY:
ret = pmbus_write_word_data(client, page,
LTC3815_MFR_IIN_PEAK, 0);
break;
case PMBUS_VIRT_RESET_IOUT_HISTORY:
ret = pmbus_write_word_data(client, page,
LTC3815_MFR_IOUT_PEAK, 0);
break;
case PMBUS_VIRT_RESET_VOUT_HISTORY:
ret = pmbus_write_word_data(client, page,
LTC3815_MFR_VOUT_PEAK, 0);
break;
case PMBUS_VIRT_RESET_VIN_HISTORY:
ret = pmbus_write_word_data(client, page,
LTC3815_MFR_VIN_PEAK, 0);
break;
case PMBUS_VIRT_RESET_TEMP_HISTORY:
ret = pmbus_write_word_data(client, page,
LTC3815_MFR_TEMP_PEAK, 0);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static const struct i2c_device_id ltc3815_id[] = {
{"ltc3815", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc3815_id);
static struct pmbus_driver_info ltc3815_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_CURRENT_IN] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 250,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 0,
.m[PSC_VOLTAGE_OUT] = 2,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 3,
.m[PSC_CURRENT_IN] = 1,
.b[PSC_CURRENT_IN] = 0,
.R[PSC_CURRENT_IN] = 2,
.m[PSC_CURRENT_OUT] = 1,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = 2,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 0,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
.read_byte_data = ltc3815_read_byte_data,
.read_word_data = ltc3815_read_word_data,
.write_byte = ltc3815_write_byte,
.write_word_data = ltc3815_write_word_data,
};
static int ltc3815_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int chip_id;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA))
return -ENODEV;
chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
if (chip_id < 0)
return chip_id;
if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
return -ENODEV;
return pmbus_do_probe(client, id, &ltc3815_info);
}
static struct i2c_driver ltc3815_driver = {
.driver = {
.name = "ltc3815",
},
.probe = ltc3815_probe,
.remove = pmbus_do_remove,
.id_table = ltc3815_id,
};
module_i2c_driver(ltc3815_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for LTC3815");
MODULE_LICENSE("GPL");
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