You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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 drivers for ADC128D818, LTC2945, LTC4260, and LTC4222 - Added support for LTM4676 to ltc2978 driver - Converted several drivers to use devm_hwmon_device_register_with_groups - Various cleanup in several drivers * tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (36 commits) hwmon: (pmbus/ltc2978) Add support for LTM4676 hwmon: (pmbus/ltc2978) Add new chip ID for LTC2974 hwmon: Do not accept invalid name attributes hwmon: (max6639) Use SIMPLE_DEV_PM_OPS macro hwmon: (lm95245) Make temp2_crit_hyst read-only hwmon: (lm95245) Convert to use devm_hwmon_device_register_with_groups hwmon: (lm95245) Drop useless debug message hwmon: (lm95245) Fix hysteresis temperatures hwmon: (max6639) Convert to use devm_hwmon_device_register_with_groups hwmon: (max6639) Introduce local dev variable, and reduce noisiness hwmon: (max6650) Introduce local 'dev' variable hwmon: (max6650) Drop error message after memory allocation failures hwmon: (max6650) Convert to use devm_hwmon_device_register_with_groups hwmon: (max6650) Rearrange code to no longer require forward declarations hwmon: (ltc4215) Convert to devm_hwmon_device_register_with_groups hwmon: (coretemp) Convert to use devm_hwmon_device_register_with_groups hwmon: (coretemp) Allocate platform data with devm_kzalloc hwmon: (coretemp) Use sysfs_create_group to create sysfs attributes hwmon: (ltc4245) Remove devicetree conditionals hwmon: (ltc4245) Drop debug messages ...
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
Kernel driver adc128d818
|
||||
========================
|
||||
|
||||
Supported chips:
|
||||
* Texas Instruments ADC818D818
|
||||
Prefix: 'adc818d818'
|
||||
Addresses scanned: I2C 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f
|
||||
Datasheet: Publicly available at the TI website
|
||||
http://www.ti.com/
|
||||
|
||||
Author: Guenter Roeck
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the Texas Instruments ADC128D818.
|
||||
It is described as 'ADC System Monitor with Temperature Sensor'.
|
||||
|
||||
The ADC128D818 implements one temperature sensor and seven voltage sensors.
|
||||
|
||||
Temperatures are measured in degrees Celsius. There is one set of limits.
|
||||
When the HOT Temperature Limit is crossed, this will cause an alarm that will
|
||||
be reasserted until the temperature drops below the HOT Hysteresis.
|
||||
Measurements are guaranteed between -55 and +125 degrees. The temperature
|
||||
measurement has a resolution of 0.5 degrees; the limits have a resolution
|
||||
of 1 degree.
|
||||
|
||||
Voltage sensors (also known as IN sensors) report their values in volts.
|
||||
An alarm is triggered if the voltage has crossed a programmable minimum
|
||||
or maximum limit. Note that minimum in this case always means 'closest to
|
||||
zero'; this is important for negative voltage measurements. All voltage
|
||||
inputs can measure voltages between 0 and 2.55 volts, with a resolution
|
||||
of 0.625 mV.
|
||||
|
||||
If an alarm triggers, it will remain triggered until the hardware register
|
||||
is read at least once. This means that the cause for the alarm may
|
||||
already have disappeared by the time the alarm is read. The driver
|
||||
caches the alarm status for each sensor until it is at least reported
|
||||
once, to ensure that alarms are reported to user space.
|
||||
|
||||
The ADC128D818 only updates its values approximately once per second;
|
||||
reading it more often will do no harm, but will return 'old' values.
|
||||
|
||||
In addition to the scanned address list, the chip can also be configured for
|
||||
addresses 0x35 to 0x37. Those addresses are not scanned. You have to instantiate
|
||||
the driver explicitly if the chip is configured for any of those addresses in
|
||||
your system.
|
||||
@@ -24,8 +24,12 @@ is given within a range of -127 to +127.875 degrees. Remote temperatures are
|
||||
given within a range of -127 to +255 degrees. Resolution depends on
|
||||
temperature input and range.
|
||||
|
||||
Each sensor has its own critical limit, but the hysteresis is common to all
|
||||
two channels.
|
||||
Each sensor has its own critical limit. Additionally, there is a relative
|
||||
hysteresis value common to both critical limits. To make life easier to
|
||||
user-space applications, two absolute values are exported, one for each
|
||||
channel, but these values are of course linked. Only the local hysteresis
|
||||
can be set from user-space, and the same delta applies to the remote
|
||||
hysteresis.
|
||||
|
||||
The lm95245 driver can change its update interval to a fixed set of values.
|
||||
It will round up to the next selectable interval. See the datasheet for exact
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
Kernel driver ltc2945
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Linear Technology LTC2945
|
||||
Prefix: 'ltc2945'
|
||||
Addresses scanned: -
|
||||
Datasheet:
|
||||
http://cds.linear.com/docs/en/datasheet/2945fa.pdf
|
||||
|
||||
Author: Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The LTC2945 is a rail-to-rail system monitor that measures current, voltage,
|
||||
and power consumption.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for LTC2945 devices, since there is no register
|
||||
which can be safely used to identify the chip. You will have to instantiate
|
||||
the devices explicitly.
|
||||
|
||||
Example: the following will load the driver for an LTC2945 at address 0x10
|
||||
on I2C bus #1:
|
||||
$ modprobe ltc2945
|
||||
$ echo ltc2945 0x10 > /sys/bus/i2c/devices/i2c-1/new_device
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
Voltage readings provided by this driver are reported as obtained from the ADC
|
||||
registers. If a set of voltage divider resistors is installed, calculate the
|
||||
real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the
|
||||
value of the divider resistor against the measured voltage and R2 is the value
|
||||
of the divider resistor against Ground.
|
||||
|
||||
Current reading provided by this driver is reported as obtained from the ADC
|
||||
Current Sense register. The reported value assumes that a 1 mOhm sense resistor
|
||||
is installed. If a different sense resistor is installed, calculate the real
|
||||
current by dividing the reported value by the sense resistor value in mOhm.
|
||||
|
||||
in1_input VIN voltage (mV). Voltage is measured either at
|
||||
SENSE+ or VDD pin depending on chip configuration.
|
||||
in1_min Undervoltage threshold
|
||||
in1_max Overvoltage threshold
|
||||
in1_lowest Lowest measured voltage
|
||||
in1_highest Highest measured voltage
|
||||
in1_reset_history Write 1 to reset in1 history
|
||||
in1_min_alarm Undervoltage alarm
|
||||
in1_max_alarm Overvoltage alarm
|
||||
|
||||
in2_input ADIN voltage (mV)
|
||||
in2_min Undervoltage threshold
|
||||
in2_max Overvoltage threshold
|
||||
in2_lowest Lowest measured voltage
|
||||
in2_highest Highest measured voltage
|
||||
in2_reset_history Write 1 to reset in2 history
|
||||
in2_min_alarm Undervoltage alarm
|
||||
in2_max_alarm Overvoltage alarm
|
||||
|
||||
curr1_input SENSE current (mA)
|
||||
curr1_min Undercurrent threshold
|
||||
curr1_max Overcurrent threshold
|
||||
curr1_lowest Lowest measured current
|
||||
curr1_highest Highest measured current
|
||||
curr1_reset_history Write 1 to reset curr1 history
|
||||
curr1_min_alarm Undercurrent alarm
|
||||
curr1_max_alarm Overcurrent alarm
|
||||
|
||||
power1_input Power (in uW). Power is calculated based on SENSE+/VDD
|
||||
voltage or ADIN voltage depending on chip configuration.
|
||||
power1_min Low lower threshold
|
||||
power1_max High power threshold
|
||||
power1_input_lowest Historical minimum power use
|
||||
power1_input_highest Historical maximum power use
|
||||
power1_reset_history Write 1 to reset power1 history
|
||||
power1_min_alarm Low power alarm
|
||||
power1_max_alarm High power alarm
|
||||
@@ -23,6 +23,10 @@ Supported chips:
|
||||
Prefix: 'ltc3883'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.linear.com/product/ltc3883
|
||||
* Linear Technology LTM4676
|
||||
Prefix: 'ltm4676'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.linear.com/product/ltm4676
|
||||
|
||||
Author: Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
@@ -33,7 +37,8 @@ Description
|
||||
LTC2974 is a quad digital power supply manager. LTC2978 is an octal power supply
|
||||
monitor. LTC2977 is a pin compatible replacement for LTC2978. LTC3880 is a dual
|
||||
output poly-phase step-down DC/DC controller. LTC3883 is a single phase
|
||||
step-down DC/DC controller.
|
||||
step-down DC/DC controller. LTM4676 is a dual 13A or single 26A uModule
|
||||
regulator.
|
||||
|
||||
|
||||
Usage Notes
|
||||
@@ -75,7 +80,7 @@ in[N]_label "vout[1-8]".
|
||||
LTC2974: N=2-5
|
||||
LTC2977: N=2-9
|
||||
LTC2978: N=2-9
|
||||
LTC3880: N=2-3
|
||||
LTC3880, LTM4676: N=2-3
|
||||
LTC3883: N=2
|
||||
in[N]_input Measured output voltage.
|
||||
in[N]_min Minimum output voltage.
|
||||
@@ -95,7 +100,7 @@ temp[N]_input Measured temperature.
|
||||
and temp5 reports the chip temperature.
|
||||
On LTC2977 and LTC2978, only one temperature measurement
|
||||
is supported and reports the chip temperature.
|
||||
On LTC3880, temp1 and temp2 report external
|
||||
On LTC3880 and LTM4676, temp1 and temp2 report external
|
||||
temperatures, and temp3 reports the chip temperature.
|
||||
On LTC3883, temp1 reports an external temperature,
|
||||
and temp2 reports the chip temperature.
|
||||
@@ -123,11 +128,11 @@ power[N]_label "pout[1-4]".
|
||||
LTC2974: N=1-4
|
||||
LTC2977: Not supported
|
||||
LTC2978: Not supported
|
||||
LTC3880: N=1-2
|
||||
LTC3880, LTM4676: N=1-2
|
||||
LTC3883: N=2
|
||||
power[N]_input Measured output power.
|
||||
|
||||
curr1_label "iin". LTC3880 and LTC3883 only.
|
||||
curr1_label "iin". LTC3880, LTC3883, and LTM4676 only.
|
||||
curr1_input Measured input current.
|
||||
curr1_max Maximum input current.
|
||||
curr1_max_alarm Input current high alarm.
|
||||
@@ -138,7 +143,7 @@ curr[N]_label "iout[1-4]".
|
||||
LTC2974: N=1-4
|
||||
LTC2977: not supported
|
||||
LTC2978: not supported
|
||||
LTC3880: N=2-3
|
||||
LTC3880, LTM4676: N=2-3
|
||||
LTC3883: N=2
|
||||
curr[N]_input Measured output current.
|
||||
curr[N]_max Maximum output current.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
Kernel driver ltc4260
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Linear Technology LTC4260
|
||||
Prefix: 'ltc4260'
|
||||
Addresses scanned: -
|
||||
Datasheet:
|
||||
http://cds.linear.com/docs/en/datasheet/4260fc.pdf
|
||||
|
||||
Author: Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The LTC4260 Hot Swap controller allows a board to be safely inserted
|
||||
and removed from a live backplane.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for LTC4260 devices, since there is no register
|
||||
which can be safely used to identify the chip. You will have to instantiate
|
||||
the devices explicitly.
|
||||
|
||||
Example: the following will load the driver for an LTC4260 at address 0x10
|
||||
on I2C bus #1:
|
||||
$ modprobe ltc4260
|
||||
$ echo ltc4260 0x10 > /sys/bus/i2c/devices/i2c-1/new_device
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
Voltage readings provided by this driver are reported as obtained from the ADC
|
||||
registers. If a set of voltage divider resistors is installed, calculate the
|
||||
real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the
|
||||
value of the divider resistor against the measured voltage and R2 is the value
|
||||
of the divider resistor against Ground.
|
||||
|
||||
Current reading provided by this driver is reported as obtained from the ADC
|
||||
Current Sense register. The reported value assumes that a 1 mOhm sense resistor
|
||||
is installed. If a different sense resistor is installed, calculate the real
|
||||
current by dividing the reported value by the sense resistor value in mOhm.
|
||||
|
||||
in1_input SOURCE voltage (mV)
|
||||
in1_min_alarm Undervoltage alarm
|
||||
in1_max_alarm Overvoltage alarm
|
||||
|
||||
in2_input ADIN voltage (mV)
|
||||
in2_alarm Power bad alarm
|
||||
|
||||
curr1_input SENSE current (mA)
|
||||
curr1_alarm SENSE overcurrent alarm
|
||||
+324
-278
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@ obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
|
||||
obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
|
||||
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
|
||||
obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o
|
||||
obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o
|
||||
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
|
||||
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
|
||||
@@ -95,9 +96,12 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o
|
||||
obj-$(CONFIG_SENSORS_LM95234) += lm95234.o
|
||||
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
|
||||
obj-$(CONFIG_SENSORS_LM95245) += lm95245.o
|
||||
obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o
|
||||
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
|
||||
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
|
||||
obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
|
||||
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
|
||||
obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o
|
||||
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
|
||||
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
||||
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
|
||||
|
||||
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Driver for TI ADC128D818 System Monitor with Temperature Sensor
|
||||
*
|
||||
* Copyright (c) 2014 Guenter Roeck
|
||||
*
|
||||
* Derived from lm80.c
|
||||
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
|
||||
* and Philip Edelbrock <phil@netroedge.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/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/* Addresses to scan
|
||||
* The chip also supports addresses 0x35..0x37. Don't scan those addresses
|
||||
* since they are also used by some EEPROMs, which may result in false
|
||||
* positives.
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = {
|
||||
0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
|
||||
|
||||
/* registers */
|
||||
#define ADC128_REG_IN_MAX(nr) (0x2a + (nr) * 2)
|
||||
#define ADC128_REG_IN_MIN(nr) (0x2b + (nr) * 2)
|
||||
#define ADC128_REG_IN(nr) (0x20 + (nr))
|
||||
|
||||
#define ADC128_REG_TEMP 0x27
|
||||
#define ADC128_REG_TEMP_MAX 0x38
|
||||
#define ADC128_REG_TEMP_HYST 0x39
|
||||
|
||||
#define ADC128_REG_CONFIG 0x00
|
||||
#define ADC128_REG_ALARM 0x01
|
||||
#define ADC128_REG_MASK 0x03
|
||||
#define ADC128_REG_CONV_RATE 0x07
|
||||
#define ADC128_REG_ONESHOT 0x09
|
||||
#define ADC128_REG_SHUTDOWN 0x0a
|
||||
#define ADC128_REG_CONFIG_ADV 0x0b
|
||||
#define ADC128_REG_BUSY_STATUS 0x0c
|
||||
|
||||
#define ADC128_REG_MAN_ID 0x3e
|
||||
#define ADC128_REG_DEV_ID 0x3f
|
||||
|
||||
struct adc128_data {
|
||||
struct i2c_client *client;
|
||||
struct regulator *regulator;
|
||||
int vref; /* Reference voltage in mV */
|
||||
struct mutex update_lock;
|
||||
bool valid; /* true if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u16 in[3][7]; /* Register value, normalized to 12 bit
|
||||
* 0: input voltage
|
||||
* 1: min limit
|
||||
* 2: max limit
|
||||
*/
|
||||
s16 temp[3]; /* Register value, normalized to 9 bit
|
||||
* 0: sensor 1: limit 2: hyst
|
||||
*/
|
||||
u8 alarms; /* alarm register value */
|
||||
};
|
||||
|
||||
static struct adc128_data *adc128_update_device(struct device *dev)
|
||||
{
|
||||
struct adc128_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct adc128_data *ret = data;
|
||||
int i, rv;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
for (i = 0; i < 7; i++) {
|
||||
rv = i2c_smbus_read_word_swapped(client,
|
||||
ADC128_REG_IN(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[0][i] = rv >> 4;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client,
|
||||
ADC128_REG_IN_MIN(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[1][i] = rv << 4;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client,
|
||||
ADC128_REG_IN_MAX(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->in[2][i] = rv << 4;
|
||||
}
|
||||
|
||||
rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp[0] = rv >> 7;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp[1] = rv << 1;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->temp[2] = rv << 1;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->alarms |= rv;
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
}
|
||||
goto done;
|
||||
|
||||
abort:
|
||||
ret = ERR_PTR(rv);
|
||||
data->valid = false;
|
||||
done:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adc128_show_in(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct adc128_data *data = adc128_update_device(dev);
|
||||
int index = to_sensor_dev_attr_2(attr)->index;
|
||||
int nr = to_sensor_dev_attr_2(attr)->nr;
|
||||
int val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = DIV_ROUND_CLOSEST(data->in[index][nr] * data->vref, 4095);
|
||||
return sprintf(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t adc128_set_in(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct adc128_data *data = dev_get_drvdata(dev);
|
||||
int index = to_sensor_dev_attr_2(attr)->index;
|
||||
int nr = to_sensor_dev_attr_2(attr)->nr;
|
||||
u8 reg, regval;
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
/* 10 mV LSB on limit registers */
|
||||
regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255);
|
||||
data->in[index][nr] = regval << 4;
|
||||
reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr);
|
||||
i2c_smbus_write_byte_data(data->client, reg, regval);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t adc128_show_temp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adc128_data *data = adc128_update_device(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
int temp;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
temp = (data->temp[index] << 7) >> 7; /* sign extend */
|
||||
return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */
|
||||
}
|
||||
|
||||
static ssize_t adc128_set_temp(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct adc128_data *data = dev_get_drvdata(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
long val;
|
||||
int err;
|
||||
s8 regval;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
|
||||
data->temp[index] = regval << 1;
|
||||
i2c_smbus_write_byte_data(data->client,
|
||||
index == 1 ? ADC128_REG_TEMP_MAX
|
||||
: ADC128_REG_TEMP_HYST,
|
||||
regval);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t adc128_show_alarm(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adc128_data *data = adc128_update_device(dev);
|
||||
int mask = 1 << to_sensor_dev_attr(attr)->index;
|
||||
u8 alarms;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
/*
|
||||
* Clear an alarm after reporting it to user space. If it is still
|
||||
* active, the next update sequence will set the alarm bit again.
|
||||
*/
|
||||
alarms = data->alarms;
|
||||
data->alarms &= ~mask;
|
||||
|
||||
return sprintf(buf, "%u\n", !!(alarms & mask));
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in0_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 0, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 0, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 0, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in1_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 1, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 1, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 1, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in2_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 2, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 2, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 2, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in3_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 3, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 3, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 3, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in4_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 4, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 4, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 4, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in5_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 5, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 5, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 5, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in6_input, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 6, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 6, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_in, adc128_set_in, 6, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc128_show_temp, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
|
||||
adc128_show_temp, adc128_set_temp, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
|
||||
adc128_show_temp, adc128_set_temp, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, adc128_show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, adc128_show_alarm, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, adc128_show_alarm, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, adc128_show_alarm, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, adc128_show_alarm, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, adc128_show_alarm, NULL, 5);
|
||||
static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, adc128_show_alarm, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adc128_show_alarm, NULL, 7);
|
||||
|
||||
static struct attribute *adc128_attrs[] = {
|
||||
&sensor_dev_attr_in0_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in5_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in5_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in5_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(adc128);
|
||||
|
||||
static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info)
|
||||
{
|
||||
int man_id, dev_id;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
man_id = i2c_smbus_read_byte_data(client, ADC128_REG_MAN_ID);
|
||||
dev_id = i2c_smbus_read_byte_data(client, ADC128_REG_DEV_ID);
|
||||
if (man_id != 0x01 || dev_id != 0x09)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check unused bits for confirmation */
|
||||
if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG) & 0xf4)
|
||||
return -ENODEV;
|
||||
if (i2c_smbus_read_byte_data(client, ADC128_REG_CONV_RATE) & 0xfe)
|
||||
return -ENODEV;
|
||||
if (i2c_smbus_read_byte_data(client, ADC128_REG_ONESHOT) & 0xfe)
|
||||
return -ENODEV;
|
||||
if (i2c_smbus_read_byte_data(client, ADC128_REG_SHUTDOWN) & 0xfe)
|
||||
return -ENODEV;
|
||||
if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV) & 0xf8)
|
||||
return -ENODEV;
|
||||
if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc)
|
||||
return -ENODEV;
|
||||
|
||||
strlcpy(info->type, "adc128d818", I2C_NAME_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc128_init_client(struct adc128_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Reset chip to defaults.
|
||||
* This makes most other initializations unnecessary.
|
||||
*/
|
||||
err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Start monitoring */
|
||||
err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* If external vref is selected, configure the chip to use it */
|
||||
if (data->regulator) {
|
||||
err = i2c_smbus_write_byte_data(client,
|
||||
ADC128_REG_CONFIG_ADV, 0x01);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc128_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct regulator *regulator;
|
||||
struct device *hwmon_dev;
|
||||
struct adc128_data *data;
|
||||
int err, vref;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct adc128_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* vref is optional. If specified, is used as chip reference voltage */
|
||||
regulator = devm_regulator_get_optional(dev, "vref");
|
||||
if (!IS_ERR(regulator)) {
|
||||
data->regulator = regulator;
|
||||
err = regulator_enable(regulator);
|
||||
if (err < 0)
|
||||
return err;
|
||||
vref = regulator_get_voltage(regulator);
|
||||
if (vref < 0) {
|
||||
err = vref;
|
||||
goto error;
|
||||
}
|
||||
data->vref = DIV_ROUND_CLOSEST(vref, 1000);
|
||||
} else {
|
||||
data->vref = 2560; /* 2.56V, in mV */
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the chip */
|
||||
err = adc128_init_client(data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, adc128_groups);
|
||||
if (IS_ERR(hwmon_dev)) {
|
||||
err = PTR_ERR(hwmon_dev);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (data->regulator)
|
||||
regulator_disable(data->regulator);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int adc128_remove(struct i2c_client *client)
|
||||
{
|
||||
struct adc128_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (data->regulator)
|
||||
regulator_disable(data->regulator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adc128_id[] = {
|
||||
{ "adc128d818", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adc128_id);
|
||||
|
||||
static struct i2c_driver adc128_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "adc128d818",
|
||||
},
|
||||
.probe = adc128_probe,
|
||||
.remove = adc128_remove,
|
||||
.id_table = adc128_id,
|
||||
.detect = adc128_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(adc128_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("Driver for ADC128D818");
|
||||
MODULE_LICENSE("GPL");
|
||||
+16
-55
@@ -94,6 +94,8 @@ struct temp_data {
|
||||
bool valid;
|
||||
struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
|
||||
char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
|
||||
struct attribute *attrs[TOTAL_ATTRS + 1];
|
||||
struct attribute_group attr_group;
|
||||
struct mutex update_lock;
|
||||
};
|
||||
|
||||
@@ -114,12 +116,6 @@ struct pdev_entry {
|
||||
static LIST_HEAD(pdev_list);
|
||||
static DEFINE_MUTEX(pdev_list_mutex);
|
||||
|
||||
static ssize_t show_name(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", DRVNAME);
|
||||
}
|
||||
|
||||
static ssize_t show_label(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
@@ -393,20 +389,10 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
return adjust_tjmax(c, id, dev);
|
||||
}
|
||||
|
||||
static int create_name_attr(struct platform_data *pdata,
|
||||
struct device *dev)
|
||||
{
|
||||
sysfs_attr_init(&pdata->name_attr.attr);
|
||||
pdata->name_attr.attr.name = "name";
|
||||
pdata->name_attr.attr.mode = S_IRUGO;
|
||||
pdata->name_attr.show = show_name;
|
||||
return device_create_file(dev, &pdata->name_attr);
|
||||
}
|
||||
|
||||
static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
||||
int attr_no)
|
||||
{
|
||||
int err, i;
|
||||
int i;
|
||||
static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev,
|
||||
struct device_attribute *devattr, char *buf) = {
|
||||
show_label, show_crit_alarm, show_temp, show_tjmax,
|
||||
@@ -424,16 +410,10 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
||||
tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
|
||||
tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
|
||||
tdata->sd_attrs[i].index = attr_no;
|
||||
err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr;
|
||||
}
|
||||
return 0;
|
||||
|
||||
exit_free:
|
||||
while (--i >= 0)
|
||||
device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
|
||||
return err;
|
||||
tdata->attr_group.attrs = tdata->attrs;
|
||||
return sysfs_create_group(&dev->kobj, &tdata->attr_group);
|
||||
}
|
||||
|
||||
|
||||
@@ -548,7 +528,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
|
||||
pdata->core_data[attr_no] = tdata;
|
||||
|
||||
/* Create sysfs interfaces */
|
||||
err = create_core_attrs(tdata, &pdev->dev, attr_no);
|
||||
err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
@@ -573,14 +553,12 @@ static void coretemp_add_core(unsigned int cpu, int pkg_flag)
|
||||
}
|
||||
|
||||
static void coretemp_remove_core(struct platform_data *pdata,
|
||||
struct device *dev, int indx)
|
||||
int indx)
|
||||
{
|
||||
int i;
|
||||
struct temp_data *tdata = pdata->core_data[indx];
|
||||
|
||||
/* Remove the sysfs attributes */
|
||||
for (i = 0; i < tdata->attr_size; i++)
|
||||
device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
|
||||
sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group);
|
||||
|
||||
kfree(pdata->core_data[indx]);
|
||||
pdata->core_data[indx] = NULL;
|
||||
@@ -588,34 +566,20 @@ static void coretemp_remove_core(struct platform_data *pdata,
|
||||
|
||||
static int coretemp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct platform_data *pdata;
|
||||
int err;
|
||||
|
||||
/* Initialize the per-package data structures */
|
||||
pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL);
|
||||
pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
err = create_name_attr(pdata, &pdev->dev);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
pdata->phys_proc_id = pdev->id;
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||
if (IS_ERR(pdata->hwmon_dev)) {
|
||||
err = PTR_ERR(pdata->hwmon_dev);
|
||||
dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
|
||||
goto exit_name;
|
||||
}
|
||||
return 0;
|
||||
|
||||
exit_name:
|
||||
device_remove_file(&pdev->dev, &pdata->name_attr);
|
||||
exit_free:
|
||||
kfree(pdata);
|
||||
return err;
|
||||
pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME,
|
||||
pdata, NULL);
|
||||
return PTR_ERR_OR_ZERO(pdata->hwmon_dev);
|
||||
}
|
||||
|
||||
static int coretemp_remove(struct platform_device *pdev)
|
||||
@@ -625,11 +589,8 @@ static int coretemp_remove(struct platform_device *pdev)
|
||||
|
||||
for (i = MAX_CORE_DATA - 1; i >= 0; --i)
|
||||
if (pdata->core_data[i])
|
||||
coretemp_remove_core(pdata, &pdev->dev, i);
|
||||
coretemp_remove_core(pdata, i);
|
||||
|
||||
device_remove_file(&pdev->dev, &pdata->name_attr);
|
||||
hwmon_device_unregister(pdata->hwmon_dev);
|
||||
kfree(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -777,7 +738,7 @@ static void put_core_offline(unsigned int cpu)
|
||||
return;
|
||||
|
||||
if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu)
|
||||
coretemp_remove_core(pdata, &pdev->dev, indx);
|
||||
coretemp_remove_core(pdata, indx);
|
||||
|
||||
/*
|
||||
* If a HT sibling of a core is taken offline, but another HT sibling
|
||||
|
||||
@@ -349,7 +349,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
|
||||
dev_dbg(&client->dev, "reg 0x%02x, err %d\n",
|
||||
REG_FAN_CONF1, status);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return -EIO;
|
||||
return status;
|
||||
}
|
||||
status &= 0x9F;
|
||||
status |= (new_range_bits << 5);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define HWMON_ID_PREFIX "hwmon"
|
||||
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
|
||||
@@ -99,6 +100,10 @@ hwmon_device_register_with_groups(struct device *dev, const char *name,
|
||||
struct hwmon_device *hwdev;
|
||||
int err, id;
|
||||
|
||||
/* Do not accept invalid characters in hwmon name attribute */
|
||||
if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
return ERR_PTR(id);
|
||||
|
||||
+12
-25
@@ -31,6 +31,7 @@ struct iio_hwmon_state {
|
||||
int num_channels;
|
||||
struct device *hwmon_dev;
|
||||
struct attribute_group attr_group;
|
||||
const struct attribute_group *groups[2];
|
||||
struct attribute **attrs;
|
||||
};
|
||||
|
||||
@@ -56,19 +57,6 @@ static ssize_t iio_hwmon_read_val(struct device *dev,
|
||||
return sprintf(buf, "%d\n", result);
|
||||
}
|
||||
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const char *name = "iio_hwmon";
|
||||
|
||||
if (dev->of_node && dev->of_node->name)
|
||||
name = dev->of_node->name;
|
||||
|
||||
return sprintf(buf, "%s\n", name);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -78,6 +66,10 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
int in_i = 1, temp_i = 1, curr_i = 1;
|
||||
enum iio_chan_type type;
|
||||
struct iio_channel *channels;
|
||||
const char *name = "iio_hwmon";
|
||||
|
||||
if (dev->of_node && dev->of_node->name)
|
||||
name = dev->of_node->name;
|
||||
|
||||
channels = iio_channel_get_all(dev);
|
||||
if (IS_ERR(channels))
|
||||
@@ -96,7 +88,7 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
st->num_channels++;
|
||||
|
||||
st->attrs = devm_kzalloc(dev,
|
||||
sizeof(*st->attrs) * (st->num_channels + 2),
|
||||
sizeof(*st->attrs) * (st->num_channels + 1),
|
||||
GFP_KERNEL);
|
||||
if (st->attrs == NULL) {
|
||||
ret = -ENOMEM;
|
||||
@@ -144,22 +136,18 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
||||
a->index = i;
|
||||
st->attrs[i] = &a->dev_attr.attr;
|
||||
}
|
||||
st->attrs[st->num_channels] = &dev_attr_name.attr;
|
||||
st->attr_group.attrs = st->attrs;
|
||||
platform_set_drvdata(pdev, st);
|
||||
ret = sysfs_create_group(&dev->kobj, &st->attr_group);
|
||||
if (ret < 0)
|
||||
goto error_release_channels;
|
||||
|
||||
st->hwmon_dev = hwmon_device_register(dev);
|
||||
st->attr_group.attrs = st->attrs;
|
||||
st->groups[0] = &st->attr_group;
|
||||
st->hwmon_dev = hwmon_device_register_with_groups(dev, name, st,
|
||||
st->groups);
|
||||
if (IS_ERR(st->hwmon_dev)) {
|
||||
ret = PTR_ERR(st->hwmon_dev);
|
||||
goto error_remove_group;
|
||||
goto error_release_channels;
|
||||
}
|
||||
platform_set_drvdata(pdev, st);
|
||||
return 0;
|
||||
|
||||
error_remove_group:
|
||||
sysfs_remove_group(&dev->kobj, &st->attr_group);
|
||||
error_release_channels:
|
||||
iio_channel_release_all(channels);
|
||||
return ret;
|
||||
@@ -170,7 +158,6 @@ static int iio_hwmon_remove(struct platform_device *pdev)
|
||||
struct iio_hwmon_state *st = platform_get_drvdata(pdev);
|
||||
|
||||
hwmon_device_unregister(st->hwmon_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &st->attr_group);
|
||||
iio_channel_release_all(st->channels);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <linux/hwmon.h>
|
||||
|
||||
struct jz4740_hwmon {
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
|
||||
int irq;
|
||||
@@ -106,6 +105,7 @@ static int jz4740_hwmon_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct jz4740_hwmon *hwmon;
|
||||
struct resource *mem;
|
||||
|
||||
hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
|
||||
if (!hwmon)
|
||||
@@ -120,25 +120,10 @@ static int jz4740_hwmon_probe(struct platform_device *pdev)
|
||||
return hwmon->irq;
|
||||
}
|
||||
|
||||
hwmon->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!hwmon->mem) {
|
||||
dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
hwmon->mem = devm_request_mem_region(&pdev->dev, hwmon->mem->start,
|
||||
resource_size(hwmon->mem), pdev->name);
|
||||
if (!hwmon->mem) {
|
||||
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
hwmon->base = devm_ioremap_nocache(&pdev->dev, hwmon->mem->start,
|
||||
resource_size(hwmon->mem));
|
||||
if (!hwmon->base) {
|
||||
dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hwmon->base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(hwmon->base))
|
||||
return PTR_ERR(hwmon->base);
|
||||
|
||||
init_completion(&hwmon->read_completion);
|
||||
mutex_init(&hwmon->lock);
|
||||
|
||||
+30
-63
@@ -89,7 +89,7 @@ static const u8 lm95241_reg_address[] = {
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct lm95241_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
unsigned long last_updated, interval; /* in jiffies */
|
||||
char valid; /* zero until following fields are valid */
|
||||
@@ -113,8 +113,8 @@ static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
|
||||
|
||||
static struct lm95241_data *lm95241_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@@ -122,7 +122,7 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
|
||||
!data->valid) {
|
||||
int i;
|
||||
|
||||
dev_dbg(&client->dev, "Updating lm95241 data.\n");
|
||||
dev_dbg(dev, "Updating lm95241 data.\n");
|
||||
for (i = 0; i < ARRAY_SIZE(lm95241_reg_address); i++)
|
||||
data->temp[i]
|
||||
= i2c_smbus_read_byte_data(client,
|
||||
@@ -153,8 +153,7 @@ static ssize_t show_input(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t show_type(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1,
|
||||
data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n");
|
||||
@@ -163,8 +162,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_type(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
int shift;
|
||||
u8 mask = to_sensor_dev_attr(attr)->index;
|
||||
@@ -201,8 +200,7 @@ static ssize_t set_type(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t show_min(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1,
|
||||
data->config & to_sensor_dev_attr(attr)->index ?
|
||||
@@ -212,8 +210,7 @@ static ssize_t show_min(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_min(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
long val;
|
||||
|
||||
if (kstrtol(buf, 10, &val) < 0)
|
||||
@@ -229,7 +226,8 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr,
|
||||
data->config &= ~to_sensor_dev_attr(attr)->index;
|
||||
data->valid = 0;
|
||||
|
||||
i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
|
||||
i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
|
||||
data->config);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
@@ -239,8 +237,7 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t show_max(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1,
|
||||
data->config & to_sensor_dev_attr(attr)->index ?
|
||||
@@ -250,8 +247,7 @@ static ssize_t show_max(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_max(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
long val;
|
||||
|
||||
if (kstrtol(buf, 10, &val) < 0)
|
||||
@@ -267,7 +263,8 @@ static ssize_t set_max(struct device *dev, struct device_attribute *attr,
|
||||
data->config &= ~to_sensor_dev_attr(attr)->index;
|
||||
data->valid = 0;
|
||||
|
||||
i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
|
||||
i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
|
||||
data->config);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
@@ -286,8 +283,7 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
struct lm95241_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
@@ -316,7 +312,7 @@ static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max,
|
||||
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
|
||||
set_interval);
|
||||
|
||||
static struct attribute *lm95241_attributes[] = {
|
||||
static struct attribute *lm95241_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
@@ -329,10 +325,7 @@ static struct attribute *lm95241_attributes[] = {
|
||||
&dev_attr_update_interval.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm95241_group = {
|
||||
.attrs = lm95241_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm95241);
|
||||
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
static int lm95241_detect(struct i2c_client *new_client,
|
||||
@@ -366,14 +359,11 @@ static int lm95241_detect(struct i2c_client *new_client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lm95241_init_client(struct i2c_client *client)
|
||||
static void lm95241_init_client(struct i2c_client *client,
|
||||
struct lm95241_data *data)
|
||||
{
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
|
||||
data->interval = HZ; /* 1 sec default */
|
||||
data->valid = 0;
|
||||
data->config = CFG_CR0076;
|
||||
data->model = 0;
|
||||
data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT);
|
||||
|
||||
i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
|
||||
@@ -385,49 +375,27 @@ static void lm95241_init_client(struct i2c_client *client)
|
||||
data->model);
|
||||
}
|
||||
|
||||
static int lm95241_probe(struct i2c_client *new_client,
|
||||
static int lm95241_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct lm95241_data *data;
|
||||
int err;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct lm95241_data),
|
||||
GFP_KERNEL);
|
||||
data = devm_kzalloc(dev, sizeof(struct lm95241_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(new_client, data);
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the LM95241 chip */
|
||||
lm95241_init_client(new_client);
|
||||
lm95241_init_client(client, data);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&new_client->dev.kobj, &lm95241_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm95241_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lm95241_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm95241_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm95241_group);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
lm95241_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
/* Driver data (common to all clients) */
|
||||
@@ -444,7 +412,6 @@ static struct i2c_driver lm95241_driver = {
|
||||
.name = DEVNAME,
|
||||
},
|
||||
.probe = lm95241_probe,
|
||||
.remove = lm95241_remove,
|
||||
.id_table = lm95241_id,
|
||||
.detect = lm95241_detect,
|
||||
.address_list = normal_i2c,
|
||||
|
||||
+46
-66
@@ -115,7 +115,7 @@ static const u8 lm95245_reg_address[] = {
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct lm95245_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
unsigned long interval; /* in msecs */
|
||||
@@ -140,8 +140,8 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l)
|
||||
|
||||
static struct lm95245_data *lm95245_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
struct lm95245_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@@ -149,7 +149,6 @@ static struct lm95245_data *lm95245_update_device(struct device *dev)
|
||||
+ msecs_to_jiffies(data->interval)) || !data->valid) {
|
||||
int i;
|
||||
|
||||
dev_dbg(&client->dev, "Updating lm95245 data.\n");
|
||||
for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
|
||||
data->regs[i]
|
||||
= i2c_smbus_read_byte_data(client,
|
||||
@@ -249,9 +248,9 @@ static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
struct lm95245_data *data = dev_get_drvdata(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
@@ -272,27 +271,38 @@ static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lm95245_data *data = lm95245_update_device(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
int hyst = data->regs[index] - data->regs[8];
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000);
|
||||
}
|
||||
|
||||
static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
struct lm95245_data *data = dev_get_drvdata(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
int hyst, limit;
|
||||
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
val /= 1000;
|
||||
|
||||
val = clamp_val(val, 0, 31);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
data->valid = 0;
|
||||
limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]);
|
||||
hyst = limit - val / 1000;
|
||||
hyst = clamp_val(hyst, 0, 31);
|
||||
data->regs[8] = hyst;
|
||||
|
||||
/* shared crit hysteresis */
|
||||
i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS,
|
||||
val);
|
||||
hyst);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
@@ -302,8 +312,7 @@ static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t show_type(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
struct lm95245_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1,
|
||||
data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n");
|
||||
@@ -312,8 +321,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_type(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
struct lm95245_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
@@ -359,8 +368,8 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
struct lm95245_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
@@ -378,16 +387,15 @@ static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit,
|
||||
set_limit, 6);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
|
||||
set_crit_hyst, 8);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst,
|
||||
set_crit_hyst, 6);
|
||||
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
STATUS1_LOC);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit,
|
||||
set_limit, 7);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
|
||||
set_crit_hyst, 8);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
STATUS1_RTCRIT);
|
||||
static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type,
|
||||
@@ -398,7 +406,7 @@ static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
|
||||
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
|
||||
set_interval);
|
||||
|
||||
static struct attribute *lm95245_attributes[] = {
|
||||
static struct attribute *lm95245_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
|
||||
@@ -412,10 +420,7 @@ static struct attribute *lm95245_attributes[] = {
|
||||
&dev_attr_update_interval.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm95245_group = {
|
||||
.attrs = lm95245_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm95245);
|
||||
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
static int lm95245_detect(struct i2c_client *new_client,
|
||||
@@ -436,11 +441,9 @@ static int lm95245_detect(struct i2c_client *new_client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lm95245_init_client(struct i2c_client *client)
|
||||
static void lm95245_init_client(struct i2c_client *client,
|
||||
struct lm95245_data *data)
|
||||
{
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
|
||||
data->valid = 0;
|
||||
data->interval = lm95245_read_conversion_rate(client);
|
||||
|
||||
data->config1 = i2c_smbus_read_byte_data(client,
|
||||
@@ -456,49 +459,27 @@ static void lm95245_init_client(struct i2c_client *client)
|
||||
}
|
||||
}
|
||||
|
||||
static int lm95245_probe(struct i2c_client *new_client,
|
||||
static int lm95245_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct lm95245_data *data;
|
||||
int err;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct lm95245_data),
|
||||
GFP_KERNEL);
|
||||
data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(new_client, data);
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the LM95245 chip */
|
||||
lm95245_init_client(new_client);
|
||||
lm95245_init_client(client, data);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&new_client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm95245_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lm95245_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm95245_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm95245_group);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
lm95245_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
/* Driver data (common to all clients) */
|
||||
@@ -514,7 +495,6 @@ static struct i2c_driver lm95245_driver = {
|
||||
.name = DEVNAME,
|
||||
},
|
||||
.probe = lm95245_probe,
|
||||
.remove = lm95245_remove,
|
||||
.id_table = lm95245_id,
|
||||
.detect = lm95245_detect,
|
||||
.address_list = normal_i2c,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+13
-38
@@ -33,7 +33,7 @@ enum ltc4215_cmd {
|
||||
};
|
||||
|
||||
struct ltc4215_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
@@ -45,8 +45,8 @@ struct ltc4215_data {
|
||||
|
||||
static struct ltc4215_data *ltc4215_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ltc4215_data *data = i2c_get_clientdata(client);
|
||||
struct ltc4215_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
s32 val;
|
||||
int i;
|
||||
|
||||
@@ -214,7 +214,7 @@ static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
|
||||
* Finally, construct an array of pointers to members of the above objects,
|
||||
* as required for sysfs_create_group()
|
||||
*/
|
||||
static struct attribute *ltc4215_attributes[] = {
|
||||
static struct attribute *ltc4215_attrs[] = {
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
|
||||
|
||||
@@ -229,57 +229,33 @@ static struct attribute *ltc4215_attributes[] = {
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ltc4215_group = {
|
||||
.attrs = ltc4215_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ltc4215);
|
||||
|
||||
static int ltc4215_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct ltc4215_data *data;
|
||||
int ret;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the LTC4215 chip */
|
||||
i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
ret = sysfs_create_group(&client->dev.kobj, <c4215_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
ret = PTR_ERR(data->hwmon_dev);
|
||||
goto out_hwmon_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_hwmon_device_register:
|
||||
sysfs_remove_group(&client->dev.kobj, <c4215_group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc4215_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ltc4215_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, <c4215_group);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
ltc4215_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc4215_id[] = {
|
||||
@@ -294,7 +270,6 @@ static struct i2c_driver ltc4215_driver = {
|
||||
.name = "ltc4215",
|
||||
},
|
||||
.probe = ltc4215_probe,
|
||||
.remove = ltc4215_remove,
|
||||
.id_table = ltc4215_id,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Driver for Linear Technology LTC4222 Dual Hot Swap controller
|
||||
*
|
||||
* Copyright (c) 2014 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* chip registers */
|
||||
|
||||
#define LTC4222_CONTROL1 0xd0
|
||||
#define LTC4222_ALERT1 0xd1
|
||||
#define LTC4222_STATUS1 0xd2
|
||||
#define LTC4222_FAULT1 0xd3
|
||||
#define LTC4222_CONTROL2 0xd4
|
||||
#define LTC4222_ALERT2 0xd5
|
||||
#define LTC4222_STATUS2 0xd6
|
||||
#define LTC4222_FAULT2 0xd7
|
||||
#define LTC4222_SOURCE1 0xd8
|
||||
#define LTC4222_SOURCE2 0xda
|
||||
#define LTC4222_ADIN1 0xdc
|
||||
#define LTC4222_ADIN2 0xde
|
||||
#define LTC4222_SENSE1 0xe0
|
||||
#define LTC4222_SENSE2 0xe2
|
||||
#define LTC4222_ADC_CONTROL 0xe4
|
||||
|
||||
/*
|
||||
* Fault register bits
|
||||
*/
|
||||
#define FAULT_OV BIT(0)
|
||||
#define FAULT_UV BIT(1)
|
||||
#define FAULT_OC BIT(2)
|
||||
#define FAULT_POWER_BAD BIT(3)
|
||||
#define FAULT_FET_BAD BIT(5)
|
||||
|
||||
/* Return the voltage from the given register in mV or mA */
|
||||
static int ltc4222_get_value(struct device *dev, u8 reg)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
u8 buf[2];
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, reg, buf, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = ((buf[0] << 8) + buf[1]) >> 6;
|
||||
|
||||
switch (reg) {
|
||||
case LTC4222_ADIN1:
|
||||
case LTC4222_ADIN2:
|
||||
/* 1.25 mV resolution. Convert to mV. */
|
||||
val = DIV_ROUND_CLOSEST(val * 5, 4);
|
||||
break;
|
||||
case LTC4222_SOURCE1:
|
||||
case LTC4222_SOURCE2:
|
||||
/* 31.25 mV resolution. Convert to mV. */
|
||||
val = DIV_ROUND_CLOSEST(val * 125, 4);
|
||||
break;
|
||||
case LTC4222_SENSE1:
|
||||
case LTC4222_SENSE2:
|
||||
/*
|
||||
* 62.5 uV resolution. Convert to current as measured with
|
||||
* an 1 mOhm sense resistor, in mA. If a different sense
|
||||
* resistor is installed, calculate the actual current by
|
||||
* dividing the reported current by the sense resistor value
|
||||
* in mOhm.
|
||||
*/
|
||||
val = DIV_ROUND_CLOSEST(val * 125, 2);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static ssize_t ltc4222_show_value(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int value;
|
||||
|
||||
value = ltc4222_get_value(dev, attr->index);
|
||||
if (value < 0)
|
||||
return value;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t ltc4222_show_bool(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
unsigned int fault;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, attr->nr, &fault);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fault &= attr->index;
|
||||
if (fault) /* Clear reported faults in chip register */
|
||||
regmap_update_bits(regmap, attr->nr, attr->index, 0);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
|
||||
}
|
||||
|
||||
/* Voltages */
|
||||
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4222_show_value, NULL,
|
||||
LTC4222_SOURCE1);
|
||||
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4222_show_value, NULL,
|
||||
LTC4222_ADIN1);
|
||||
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4222_show_value, NULL,
|
||||
LTC4222_SOURCE2);
|
||||
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4222_show_value, NULL,
|
||||
LTC4222_ADIN2);
|
||||
|
||||
/*
|
||||
* Voltage alarms
|
||||
* UV/OV faults are associated with the input voltage, and power bad and fet
|
||||
* faults are associated with the output voltage.
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT1, FAULT_UV);
|
||||
static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT1, FAULT_OV);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT1, FAULT_POWER_BAD | FAULT_FET_BAD);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT2, FAULT_UV);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT2, FAULT_OV);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT2, FAULT_POWER_BAD | FAULT_FET_BAD);
|
||||
|
||||
/* Current (via sense resistor) */
|
||||
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4222_show_value, NULL,
|
||||
LTC4222_SENSE1);
|
||||
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4222_show_value, NULL,
|
||||
LTC4222_SENSE2);
|
||||
|
||||
/* Overcurrent alarm */
|
||||
static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT1, FAULT_OC);
|
||||
static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
|
||||
LTC4222_FAULT2, FAULT_OC);
|
||||
|
||||
static struct attribute *ltc4222_attrs[] = {
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_curr2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
|
||||
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ltc4222);
|
||||
|
||||
static struct regmap_config ltc4222_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = LTC4222_ADC_CONTROL,
|
||||
};
|
||||
|
||||
static int ltc4222_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, <c4222_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
/* Clear faults */
|
||||
regmap_write(regmap, LTC4222_FAULT1, 0x00);
|
||||
regmap_write(regmap, LTC4222_FAULT2, 0x00);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
regmap,
|
||||
ltc4222_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc4222_id[] = {
|
||||
{"ltc4222", 0},
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ltc4222_id);
|
||||
|
||||
static struct i2c_driver ltc4222_driver = {
|
||||
.driver = {
|
||||
.name = "ltc4222",
|
||||
},
|
||||
.probe = ltc4222_probe,
|
||||
.id_table = ltc4222_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ltc4222_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("LTC4222 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
+4
-26
@@ -95,7 +95,6 @@ static void ltc4245_update_gpios(struct device *dev)
|
||||
* readings as stale by setting them to -EAGAIN
|
||||
*/
|
||||
if (time_after(jiffies, data->last_updated + 5 * HZ)) {
|
||||
dev_dbg(&client->dev, "Marking GPIOs invalid\n");
|
||||
for (i = 0; i < ARRAY_SIZE(data->gpios); i++)
|
||||
data->gpios[i] = -EAGAIN;
|
||||
}
|
||||
@@ -141,8 +140,6 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
|
||||
dev_dbg(&client->dev, "Starting ltc4245 update\n");
|
||||
|
||||
/* Read control registers -- 0x00 to 0x07 */
|
||||
for (i = 0; i < ARRAY_SIZE(data->cregs); i++) {
|
||||
val = i2c_smbus_read_byte_data(client, i);
|
||||
@@ -470,19 +467,15 @@ static void ltc4245_sysfs_add_groups(struct ltc4245_data *data)
|
||||
static bool ltc4245_use_extra_gpios(struct i2c_client *client)
|
||||
{
|
||||
struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
#ifdef CONFIG_OF
|
||||
struct device_node *np = client->dev.of_node;
|
||||
#endif
|
||||
|
||||
/* prefer platform data */
|
||||
if (pdata)
|
||||
return pdata->use_extra_gpios;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* fallback on OF */
|
||||
if (of_find_property(np, "ltc4245,use-extra-gpios", NULL))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -512,24 +505,10 @@ static int ltc4245_probe(struct i2c_client *client,
|
||||
/* Add sysfs hooks */
|
||||
ltc4245_sysfs_add_groups(data);
|
||||
|
||||
hwmon_dev = hwmon_device_register_with_groups(&client->dev,
|
||||
client->name, data,
|
||||
data->groups);
|
||||
if (IS_ERR(hwmon_dev))
|
||||
return PTR_ERR(hwmon_dev);
|
||||
|
||||
i2c_set_clientdata(client, hwmon_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc4245_remove(struct i2c_client *client)
|
||||
{
|
||||
struct device *hwmon_dev = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(hwmon_dev);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
|
||||
client->name, data,
|
||||
data->groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc4245_id[] = {
|
||||
@@ -544,7 +523,6 @@ static struct i2c_driver ltc4245_driver = {
|
||||
.name = "ltc4245",
|
||||
},
|
||||
.probe = ltc4245_probe,
|
||||
.remove = ltc4245_remove,
|
||||
.id_table = ltc4245_id,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller
|
||||
*
|
||||
* Copyright (c) 2014 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* chip registers */
|
||||
#define LTC4260_CONTROL 0x00
|
||||
#define LTC4260_ALERT 0x01
|
||||
#define LTC4260_STATUS 0x02
|
||||
#define LTC4260_FAULT 0x03
|
||||
#define LTC4260_SENSE 0x04
|
||||
#define LTC4260_SOURCE 0x05
|
||||
#define LTC4260_ADIN 0x06
|
||||
|
||||
/*
|
||||
* Fault register bits
|
||||
*/
|
||||
#define FAULT_OV (1 << 0)
|
||||
#define FAULT_UV (1 << 1)
|
||||
#define FAULT_OC (1 << 2)
|
||||
#define FAULT_POWER_BAD (1 << 3)
|
||||
#define FAULT_FET_SHORT (1 << 5)
|
||||
|
||||
/* Return the voltage from the given register in mV or mA */
|
||||
static int ltc4260_get_value(struct device *dev, u8 reg)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, reg, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (reg) {
|
||||
case LTC4260_ADIN:
|
||||
/* 10 mV resolution. Convert to mV. */
|
||||
val = val * 10;
|
||||
break;
|
||||
case LTC4260_SOURCE:
|
||||
/* 400 mV resolution. Convert to mV. */
|
||||
val = val * 400;
|
||||
break;
|
||||
case LTC4260_SENSE:
|
||||
/*
|
||||
* 300 uV resolution. Convert to current as measured with
|
||||
* an 1 mOhm sense resistor, in mA. If a different sense
|
||||
* resistor is installed, calculate the actual current by
|
||||
* dividing the reported current by the sense resistor value
|
||||
* in mOhm.
|
||||
*/
|
||||
val = val * 300;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static ssize_t ltc4260_show_value(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int value;
|
||||
|
||||
value = ltc4260_get_value(dev, attr->index);
|
||||
if (value < 0)
|
||||
return value;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t ltc4260_show_bool(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
unsigned int fault;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, LTC4260_FAULT, &fault);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fault &= attr->index;
|
||||
if (fault) /* Clear reported faults in chip register */
|
||||
regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
|
||||
}
|
||||
|
||||
/* Voltages */
|
||||
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4260_show_value, NULL,
|
||||
LTC4260_SOURCE);
|
||||
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4260_show_value, NULL,
|
||||
LTC4260_ADIN);
|
||||
|
||||
/*
|
||||
* Voltage alarms
|
||||
* UV/OV faults are associated with the input voltage, and the POWER BAD and
|
||||
* FET SHORT faults are associated with the output voltage.
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4260_show_bool, NULL,
|
||||
FAULT_UV);
|
||||
static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL,
|
||||
FAULT_OV);
|
||||
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, ltc4260_show_bool, NULL,
|
||||
FAULT_POWER_BAD | FAULT_FET_SHORT);
|
||||
|
||||
/* Current (via sense resistor) */
|
||||
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4260_show_value, NULL,
|
||||
LTC4260_SENSE);
|
||||
|
||||
/* Overcurrent alarm */
|
||||
static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL,
|
||||
FAULT_OC);
|
||||
|
||||
static struct attribute *ltc4260_attrs[] = {
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
|
||||
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ltc4260);
|
||||
|
||||
static struct regmap_config ltc4260_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = LTC4260_ADIN,
|
||||
};
|
||||
|
||||
static int ltc4260_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, <c4260_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
/* Clear faults */
|
||||
regmap_write(regmap, LTC4260_FAULT, 0x00);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
regmap,
|
||||
ltc4260_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc4260_id[] = {
|
||||
{"ltc4260", 0},
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ltc4260_id);
|
||||
|
||||
static struct i2c_driver ltc4260_driver = {
|
||||
.driver = {
|
||||
.name = "ltc4260",
|
||||
},
|
||||
.probe = ltc4260_probe,
|
||||
.id_table = ltc4260_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ltc4260_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("LTC4260 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user