Commit 24d734a2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-linus-v4.13-rc1' of...

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

Pull hwmon updates from Guenter Roeck:

 - Add PMBus client driver for IR35221

 - Add support for NCT6795D to nct6775 driver

 - Functional improvements to adt7475, aspeed-pwm-tacho, and ibmpowernv
   drivers

 - Minor fixes and cleanups in various drivers

* tag 'hwmon-for-linus-v4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (22 commits)
  hwmon: (aspeed-pwm-tacho) Poll with short sleeps.
  hwmon: (aspeed-pwm-tacho) reduce fan_tach period
  hwmon: (ibmpowernv) Add current(A) sensor
  hwmon: (ibmpowernv) introduce a legacy_compatibles array
  hwmon: (pwm-fan) Switch to new atomic PWM API
  hwmon: (scpi) Fix the scale of SCP sensor readings
  hwmon: (aspeed-pwm-tacho) Enable both edge measurement.
  hwmon: (ibmpowernv) Add highest/lowest attributes to sensors
  hwmon: (pmbus) move header file out of I2C realm
  hwmon: (max6639) move header file out of I2C realm
  hwmon: (ltc4245) move header file out of I2C realm
  hwmon: (ds620) move header file out of I2C realm
  hwmon: (ads1015) move header file out of I2C realm
  hwmon: (adt7475) temperature smoothing
  hwmon: (adt7475) add high frequency support
  hwmon: (adt7475) fan stall prevention
  hwmon: (adt7475) replace find_nearest() with find_closest()
  hwmon: (pmbus) Add client driver for IR35221
  hwmon: (nct6775) Add support for NCT6795D
  hwmon: (nct6775) Improve fan detection
  ...
parents 17ece345 44b41366
......@@ -40,7 +40,7 @@ By default all inputs are exported.
Platform Data
-------------
In linux/i2c/ads1015.h platform data is defined, channel_data contains
In linux/platform_data/ads1015.h platform data is defined, channel_data contains
configuration data for the used input combinations:
- pga is the programmable gain amplifier (values are full scale)
0: +/- 6.144 V
......
......@@ -109,6 +109,15 @@ fan speed) is applied. PWM values range from 0 (off) to 255 (full speed).
Fan speed may be set to maximum when the temperature sensor associated with
the PWM control exceeds temp#_max.
At Tmin - hysteresis the PWM output can either be off (0% duty cycle) or at the
minimum (i.e. auto_point1_pwm). This behaviour can be configured using the
pwm[1-*]_stall_disable sysfs attribute. A value of 0 means the fans will shut
off. A value of 1 means the fans will run at auto_point1_pwm.
The responsiveness of the ADT747x to temperature changes can be configured.
This allows smoothing of the fan speed transition. To set the transition time
set the value in ms in the temp[1-*]_smoothing sysfs attribute.
Notes
-----
......
Kernel driver ir35221
=====================
Supported chips:
* Infinion IR35221
Prefix: 'ir35221'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
Author: Samuel Mendoza-Jonas <sam@mendozajonas.com>
Description
-----------
IR35221 is a Digital DC-DC Multiphase 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 IR35221
at address 0x70 on I2C bus #4:
# modprobe ir35221
# echo ir35221 0x70 > /sys/bus/i2c/devices/i2c-4/new_device
Sysfs attributes
----------------
curr1_label "iin"
curr1_input Measured input current
curr1_max Maximum current
curr1_max_alarm Current high alarm
curr[2-3]_label "iout[1-2]"
curr[2-3]_input Measured output current
curr[2-3]_crit Critical maximum current
curr[2-3]_crit_alarm Current critical high alarm
curr[2-3]_highest Highest output current
curr[2-3]_lowest Lowest output current
curr[2-3]_max Maximum current
curr[2-3]_max_alarm Current high alarm
in1_label "vin"
in1_input Measured input voltage
in1_crit Critical maximum input voltage
in1_crit_alarm Input voltage critical high alarm
in1_highest Highest input voltage
in1_lowest Lowest input voltage
in1_min Minimum input voltage
in1_min_alarm Input voltage low alarm
in[2-3]_label "vout[1-2]"
in[2-3]_input Measured output voltage
in[2-3]_lcrit Critical minimum output voltage
in[2-3]_lcrit_alarm Output voltage critical low alarm
in[2-3]_crit Critical maximum output voltage
in[2-3]_crit_alarm Output voltage critical high alarm
in[2-3]_highest Highest output voltage
in[2-3]_lowest Lowest output voltage
in[2-3]_max Maximum output voltage
in[2-3]_max_alarm Output voltage high alarm
in[2-3]_min Minimum output voltage
in[2-3]_min_alarm Output voltage low alarm
power1_label "pin"
power1_input Measured input power
power1_alarm Input power high alarm
power1_max Input power limit
power[2-3]_label "pout[1-2]"
power[2-3]_input Measured output power
power[2-3]_max Output power limit
power[2-3]_max_alarm Output power high alarm
temp[1-2]_input Measured temperature
temp[1-2]_crit Critical high temperature
temp[1-2]_crit_alarm Chip temperature critical high alarm
temp[1-2]_highest Highest temperature
temp[1-2]_lowest Lowest temperature
temp[1-2]_max Maximum temperature
temp[1-2]_max_alarm Chip temperature high alarm
......@@ -96,7 +96,7 @@ slowly, -EAGAIN will be returned when you read the sysfs attribute containing
the sensor reading.
The LTC4245 chip can be configured to sample all GPIO pins with two methods:
1) platform data -- see include/linux/i2c/ltc4245.h
1) platform data -- see include/linux/platform_data/ltc4245.h
2) OF device tree -- add the "ltc4245,use-extra-gpios" property to each chip
The default mode of operation is to sample a single GPIO pin.
......@@ -253,7 +253,7 @@ Specifically, it provides the following information.
PMBus driver platform data
==========================
PMBus platform data is defined in include/linux/i2c/pmbus.h. Platform data
PMBus platform data is defined in include/linux/pmbus.h. Platform data
currently only provides a flag field with a single bit used.
#define PMBUS_SKIP_STATUS_CHECK (1 << 0)
......
......@@ -478,7 +478,7 @@ L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ads1015
F: drivers/hwmon/ads1015.c
F: include/linux/i2c/ads1015.h
F: include/linux/platform_data/ads1015.h
ADT746X FAN DRIVER
M: Colin Leroy <colin@colino.net>
......@@ -10179,7 +10179,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
S: Maintained
F: Documentation/hwmon/pmbus
F: drivers/hwmon/pmbus/
F: include/linux/i2c/pmbus.h
F: include/linux/pmbus.h
PMC SIERRA MaxRAID DRIVER
L: linux-scsi@vger.kernel.org
......
......@@ -34,7 +34,7 @@
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/i2c/ads1015.h>
#include <linux/platform_data/ads1015.h>
/* ADS1015 registers */
enum {
......
This diff is collapsed.
......@@ -146,14 +146,26 @@
#define PWM_MAX 255
#define BOTH_EDGES 0x02 /* 10b */
#define M_PWM_DIV_H 0x00
#define M_PWM_DIV_L 0x05
#define M_PWM_PERIOD 0x5F
#define M_TACH_CLK_DIV 0x00
#define M_TACH_MODE 0x00
#define M_TACH_UNIT 0x1000
/*
* 5:4 Type N fan tach mode selection bit:
* 00: falling
* 01: rising
* 10: both
* 11: reserved.
*/
#define M_TACH_MODE 0x02 /* 10b */
#define M_TACH_UNIT 0x00c0
#define INIT_FAN_CTRL 0xFF
/* How long we sleep in us while waiting for an RPM result. */
#define ASPEED_RPM_STATUS_SLEEP_USEC 500
struct aspeed_pwm_tacho_data {
struct regmap *regmap;
unsigned long clk_freq;
......@@ -163,6 +175,7 @@ struct aspeed_pwm_tacho_data {
u8 type_pwm_clock_division_h[3];
u8 type_pwm_clock_division_l[3];
u8 type_fan_tach_clock_division[3];
u8 type_fan_tach_mode[3];
u16 type_fan_tach_unit[3];
u8 pwm_port_type[8];
u8 pwm_port_fan_ctrl[8];
......@@ -498,8 +511,9 @@ static u32 aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
u8 fan_tach_ch)
{
u32 raw_data, tach_div, clk_source, sec, val;
u8 fan_tach_ch_source, type;
u32 raw_data, tach_div, clk_source, msec, usec, val;
u8 fan_tach_ch_source, type, mode, both;
int ret;
regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0x1 << fan_tach_ch);
......@@ -507,16 +521,31 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
type = priv->pwm_port_type[fan_tach_ch_source];
sec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
msleep(sec);
msec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
usec = msec * 1000;
ret = regmap_read_poll_timeout(
priv->regmap,
ASPEED_PTCR_RESULT,
val,
(val & RESULT_STATUS_MASK),
ASPEED_RPM_STATUS_SLEEP_USEC,
usec);
regmap_read(priv->regmap, ASPEED_PTCR_RESULT, &val);
if (!(val & RESULT_STATUS_MASK))
return -ETIMEDOUT;
/* return -ETIMEDOUT if we didn't get an answer. */
if (ret)
return ret;
raw_data = val & RESULT_VALUE_MASK;
tach_div = priv->type_fan_tach_clock_division[type];
tach_div = 0x4 << (tach_div * 2);
/*
* We need the mode to determine if the raw_data is double (from
* counting both edges).
*/
mode = priv->type_fan_tach_mode[type];
both = (mode & BOTH_EDGES) ? 1 : 0;
tach_div = (0x4 << both) << (tach_div * 2);
clk_source = priv->clk_freq;
if (raw_data == 0)
......@@ -702,6 +731,7 @@ static void aspeed_create_type(struct aspeed_pwm_tacho_data *priv)
aspeed_set_tacho_type_enable(priv->regmap, TYPEM, true);
priv->type_fan_tach_clock_division[TYPEM] = M_TACH_CLK_DIV;
priv->type_fan_tach_unit[TYPEM] = M_TACH_UNIT;
priv->type_fan_tach_mode[TYPEM] = M_TACH_MODE;
aspeed_set_tacho_type_values(priv->regmap, TYPEM, M_TACH_MODE,
M_TACH_UNIT, M_TACH_CLK_DIV);
}
......
......@@ -30,7 +30,7 @@
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/i2c/ds620.h>
#include <linux/platform_data/ds620.h>
/*
* Many DS620 constants specified below
......
......@@ -50,22 +50,34 @@ enum sensors {
TEMP,
POWER_SUPPLY,
POWER_INPUT,
CURRENT,
MAX_SENSOR_TYPE,
};
#define INVALID_INDEX (-1U)
/*
* 'compatible' string properties for sensor types as defined in old
* PowerNV firmware (skiboot). These are ordered as 'enum sensors'.
*/
static const char * const legacy_compatibles[] = {
"ibm,opal-sensor-cooling-fan",
"ibm,opal-sensor-amb-temp",
"ibm,opal-sensor-power-supply",
"ibm,opal-sensor-power"
};
static struct sensor_group {
const char *name;
const char *compatible;
const char *name; /* matches property 'sensor-type' */
struct attribute_group group;
u32 attr_count;
u32 hwmon_index;
} sensor_groups[] = {
{"fan", "ibm,opal-sensor-cooling-fan"},
{"temp", "ibm,opal-sensor-amb-temp"},
{"in", "ibm,opal-sensor-power-supply"},
{"power", "ibm,opal-sensor-power"}
{ "fan" },
{ "temp" },
{ "in" },
{ "power" },
{ "curr" },
};
struct sensor_data {
......@@ -239,8 +251,8 @@ static int get_sensor_type(struct device_node *np)
enum sensors type;
const char *str;
for (type = 0; type < MAX_SENSOR_TYPE; type++) {
if (of_device_is_compatible(np, sensor_groups[type].compatible))
for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) {
if (of_device_is_compatible(np, legacy_compatibles[type]))
return type;
}
......@@ -298,10 +310,14 @@ static int populate_attr_groups(struct platform_device *pdev)
sensor_groups[type].attr_count++;
/*
* add a new attribute for labels
* add attributes for labels, min and max
*/
if (!of_property_read_string(np, "label", &label))
sensor_groups[type].attr_count++;
if (of_find_property(np, "sensor-data-min", NULL))
sensor_groups[type].attr_count++;
if (of_find_property(np, "sensor-data-max", NULL))
sensor_groups[type].attr_count++;
}
of_node_put(opal);
......@@ -337,6 +353,41 @@ static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name,
sdata->dev_attr.show = show;
}
static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid,
const char *attr_name, enum sensors type,
const struct attribute_group *pgroup,
ssize_t (*show)(struct device *dev,
struct device_attribute *attr,
char *buf))
{
sdata->id = sid;
sdata->type = type;
sdata->opal_index = od;
sdata->hwmon_index = hd;
create_hwmon_attr(sdata, attr_name, show);
pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr;
}
static char *get_max_attr(enum sensors type)
{
switch (type) {
case POWER_INPUT:
return "input_highest";
default:
return "highest";
}
}
static char *get_min_attr(enum sensors type)
{
switch (type) {
case POWER_INPUT:
return "input_lowest";
default:
return "lowest";
}
}
/*
* Iterate through the device tree for each child of 'sensors' node, create
* a sysfs attribute file, the file is named by translating the DT node name
......@@ -417,16 +468,31 @@ static int create_device_attrs(struct platform_device *pdev)
* attribute. They are related to the same
* sensor.
*/
sdata[count].type = type;
sdata[count].opal_index = sdata[count - 1].opal_index;
sdata[count].hwmon_index = sdata[count - 1].hwmon_index;
make_sensor_label(np, &sdata[count], label);
populate_sensor(&sdata[count], opal_index,
sdata[count - 1].hwmon_index,
sensor_id, "label", type, pgroups[type],
show_label);
count++;
}
create_hwmon_attr(&sdata[count], "label", show_label);
if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) {
attr_name = get_max_attr(type);
populate_sensor(&sdata[count], opal_index,
sdata[count - 1].hwmon_index,
sensor_id, attr_name, type,
pgroups[type], show_sensor);
count++;
}
pgroups[type]->attrs[sensor_groups[type].attr_count++] =
&sdata[count++].dev_attr.attr;
if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) {
attr_name = get_min_attr(type);
populate_sensor(&sdata[count], opal_index,
sdata[count - 1].hwmon_index,
sensor_id, attr_name, type,
pgroups[type], show_sensor);
count++;
}
}
......
......@@ -23,7 +23,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/i2c/ltc4245.h>
#include <linux/platform_data/ltc4245.h>
/* Here are names of the chip's registers (a.k.a. commands) */
enum ltc4245_cmd {
......
......@@ -32,7 +32,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/i2c/max6639.h>
#include <linux/platform_data/max6639.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
......
This diff is collapsed.
......@@ -37,6 +37,16 @@ config SENSORS_ADM1275
This driver can also be built as a module. If so, the module will
be called adm1275.
config SENSORS_IR35221
tristate "Infineon IR35221"
default n
help
If you say yes here you get hardware monitoring support for the
Infineon IR35221 controller.
This driver can also be built as a module. If so, the module will
be called ir35521.
config SENSORS_LM25066
tristate "National Semiconductor LM25066 and compatibles"
default n
......
......@@ -5,6 +5,7 @@
obj-$(CONFIG_PMBUS) += pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
......
/*
* Hardware monitoring driver for IR35221
*
* Copyright (C) IBM Corporation 2017.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"
#define IR35221_MFR_VIN_PEAK 0xc5
#define IR35221_MFR_VOUT_PEAK 0xc6
#define IR35221_MFR_IOUT_PEAK 0xc7
#define IR35221_MFR_TEMP_PEAK 0xc8
#define IR35221_MFR_VIN_VALLEY 0xc9
#define IR35221_MFR_VOUT_VALLEY 0xca
#define IR35221_MFR_IOUT_VALLEY 0xcb
#define IR35221_MFR_TEMP_VALLEY 0xcc
static long ir35221_reg2data(int data, enum pmbus_sensor_classes class)
{
s16 exponent;
s32 mantissa;
long val;
/* We only modify LINEAR11 formats */
exponent = ((s16)data) >> 11;
mantissa = ((s16)((data & 0x7ff) << 5)) >> 5;
val = mantissa * 1000L;
/* scale result to micro-units for power sensors */
if (class == PSC_POWER)
val = val * 1000L;
if (exponent >= 0)
val <<= exponent;
else
val >>= -exponent;
return val;
}
#define MAX_MANTISSA (1023 * 1000)
#define MIN_MANTISSA (511 * 1000)
static u16 ir35221_data2reg(long val, enum pmbus_sensor_classes class)
{
s16 exponent = 0, mantissa;
bool negative = false;
if (val == 0)
return 0;
if (val < 0) {
negative = true;
val = -val;
}
/* Power is in uW. Convert to mW before converting. */
if (class == PSC_POWER)
val = DIV_ROUND_CLOSEST(val, 1000L);
/* Reduce large mantissa until it fits into 10 bit */
while (val >= MAX_MANTISSA && exponent < 15) {
exponent++;
val >>= 1;
}
/* Increase small mantissa to improve precision */
while (val < MIN_MANTISSA && exponent > -15) {
exponent--;
val <<= 1;
}
/* Convert mantissa from milli-units to units */
mantissa = DIV_ROUND_CLOSEST(val, 1000);
/* Ensure that resulting number is within range */
if (mantissa > 0x3ff)
mantissa = 0x3ff;
/* restore sign */
if (negative)
mantissa = -mantissa;
/* Convert to 5 bit exponent, 11 bit mantissa */
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
}
static u16 ir35221_scale_result(s16 data, int shift,
enum pmbus_sensor_classes class)
{
long val;
val = ir35221_reg2data(data, class);
if (shift < 0)
val >>= -shift;
else
val <<= shift;
return ir35221_data2reg(val, class);
}
static int ir35221_read_word_data(struct i2c_client *client, int page, int reg)
{
int ret;
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, reg);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, 1, PSC_CURRENT_OUT);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VIN_OV_WARN_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, reg);
ret = ir35221_scale_result(ret, -4, PSC_VOLTAGE_IN);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, reg);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_IN);
break;
case PMBUS_READ_VIN:
ret = pmbus_read_word_data(client, page, PMBUS_READ_VIN);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break;
case PMBUS_READ_IIN:
ret = pmbus_read_word_data(client, page, PMBUS_READ_IIN);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -4, PSC_CURRENT_IN);
else
ret = ir35221_scale_result(ret, -5, PSC_CURRENT_IN);
break;
case PMBUS_READ_POUT:
ret = pmbus_read_word_data(client, page, PMBUS_READ_POUT);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -1, PSC_POWER);
break;
case PMBUS_READ_PIN:
ret = pmbus_read_word_data(client, page, PMBUS_READ_PIN);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -1, PSC_POWER);
break;
case PMBUS_READ_IOUT:
ret = pmbus_read_word_data(client, page, PMBUS_READ_IOUT);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_OUT);
else
ret = ir35221_scale_result(ret, -2, PSC_CURRENT_OUT);
break;
case PMBUS_VIRT_READ_VIN_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_VIN_PEAK);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break;
case PMBUS_VIRT_READ_VOUT_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_VOUT_PEAK);
break;
case PMBUS_VIRT_READ_IOUT_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_IOUT_PEAK);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_IN);
else
ret = ir35221_scale_result(ret, -2, PSC_CURRENT_IN);
break;
case PMBUS_VIRT_READ_TEMP_MAX:
ret = pmbus_read_word_data(client, page, IR35221_MFR_TEMP_PEAK);
break;
case PMBUS_VIRT_READ_VIN_MIN:
ret = pmbus_read_word_data(client, page,
IR35221_MFR_VIN_VALLEY);
if (ret < 0)
break;
ret = ir35221_scale_result(ret, -5, PSC_VOLTAGE_IN);
break;
case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, page,
IR35221_MFR_VOUT_VALLEY);
break;
case PMBUS_VIRT_READ_IOUT_MIN:
ret = pmbus_read_word_data(client, page,
IR35221_MFR_IOUT_VALLEY);
if (ret < 0)
break;
if (page == 0)
ret = ir35221_scale_result(ret, -1, PSC_CURRENT_IN);
else
ret = ir35221_scale_result(ret, -2, PSC_CURRENT_IN);
break;
case PMBUS_VIRT_READ_TEMP_MIN:
ret = pmbus_read_word_data(client, page,
IR35221_MFR_TEMP_VALLEY);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ir35221_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
int ret;
u16 val;
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
case PMBUS_IOUT_OC_WARN_LIMIT:
val = ir35221_scale_result(word, -1, PSC_CURRENT_OUT);
ret = pmbus_write_word_data(client, page, reg, val);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VIN_OV_WARN_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
val = ir35221_scale_result(word, 4, PSC_VOLTAGE_IN);
ret = pmbus_write_word_data(client, page, reg, val);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
val = ir35221_scale_result(word, 1, PSC_CURRENT_IN);
ret = pmbus_write_word_data(client, page, reg, val);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ir35221_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pmbus_driver_info *info;
u8 buf[I2C_SMBUS_BLOCK_MAX];
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA
| I2C_FUNC_SMBUS_READ_WORD_DATA
| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
return -ENODEV;
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0) {
dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
return ret;
}
if (ret != 2 || strncmp(buf, "RI", strlen("RI"))) {
dev_err(&client->dev, "MFR_ID unrecognised\n");
return -ENODEV;
}
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
if (ret < 0) {
dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n");
return ret;
}
if (ret != 2 || !(buf[0] == 0x6c && buf[1] == 0x00)) {
dev_err(&client->dev, "MFR_MODEL unrecognised\n");
return -ENODEV;
}
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
info->write_word_data = ir35221_write_word_data;
info->read_word_data = ir35221_read_word_data;
info->pages = 2;
info->format[PSC_VOLTAGE_IN] = linear;
info->format[PSC_VOLTAGE_OUT] = linear;
info->format[PSC_CURRENT_IN] = linear;
info->format[PSC_CURRENT_OUT] = linear;
info->format[PSC_POWER] = linear;
info->format[PSC_TEMPERATURE] = linear;
info->func[0] = PMBUS_HAVE_VIN
| PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
| PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP;
info->func[1] = info->func[0];
return pmbus_do_probe(client, id, info);
}
static const struct i2c_device_id ir35221_id[] = {
{"ir35221", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ir35221_id);
static struct i2c_driver ir35221_driver = {
.driver = {
.name = "ir35221",
},
.probe = ir35221_probe,
.remove = pmbus_do_remove,
.id_table = ir35221_id,
};
module_i2c_driver(ir35221_driver);
MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
MODULE_DESCRIPTION("PMBus driver for IR35221");
MODULE_LICENSE("GPL");
......@@ -25,7 +25,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/i2c/pmbus.h>
#include <linux/pmbus.h>
#include "pmbus.h"
/*
......
......@@ -28,7 +28,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/i2c/pmbus.h>
#include <linux/pmbus.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include "pmbus.h"
......
......@@ -26,7 +26,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c/pmbus.h>
#include <linux/pmbus.h>
#include "pmbus.h"
enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
......
......@@ -25,7 +25,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c/pmbus.h>
#include <linux/pmbus.h>
#include "pmbus.h"
#define UCD9200_PHASE_INFO 0xd2
......
......@@ -40,31 +40,22 @@ struct pwm_fan_ctx {
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
struct pwm_args pargs;
unsigned long duty;
unsigned long period;
int ret = 0;
pwm_get_args(ctx->pwm, &pargs);
struct pwm_state state = { };
mutex_lock(&ctx->lock);
if (ctx->pwm_value == pwm)
goto exit_set_pwm_err;
duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM);
ret = pwm_config(ctx->pwm, duty, pargs.period);
if (ret)
goto exit_set_pwm_err;
if (pwm == 0)
pwm_disable(ctx->pwm);
if (ctx->pwm_value == 0) {
ret = pwm_enable(ctx->pwm);
if (ret)
goto exit_set_pwm_err;
}
pwm_init_state(ctx->pwm, &state);
period = ctx->pwm->args.period;
state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
state.enabled = pwm ? true : false;
ctx->pwm_value = pwm;
ret = pwm_apply_state(ctx->pwm, &state);
if (!ret)
ctx->pwm_value = pwm;
exit_set_pwm_err:
mutex_unlock(&ctx->lock);
return ret;
......@@ -218,10 +209,9 @@ static int pwm_fan_probe(struct platform_device *pdev)
{
struct thermal_cooling_device *cdev;
struct pwm_fan_ctx *ctx;
struct pwm_args pargs;
struct device *hwmon;
int duty_cycle;
int ret;
struct pwm_state state = { };
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
......@@ -237,28 +227,16 @@ static int pwm_fan_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args(ctx->pwm);
/* Set duty cycle to maximum allowed */
pwm_get_args(ctx->pwm, &pargs);
duty_cycle = pargs.period - 1;
ctx->pwm_value = MAX_PWM;
ret = pwm_config(ctx->pwm, duty_cycle, pargs.period);
if (ret) {
dev_err(&pdev->dev, "Failed to configure PWM\n");
return ret;
}
/* Set duty cycle to maximum allowed and enable PWM output */
pwm_init_state(ctx->pwm, &state);
state.duty_cycle = ctx->pwm->args.period - 1;
state.enabled = true;
/* Enbale PWM output */
ret = pwm_enable(ctx->pwm);
ret = pwm_apply_state(ctx->pwm, &state);
if (ret) {
dev_err(&pdev->dev, "Failed to enable PWM\n");
dev_err(&pdev->dev, "Failed to configure PWM\n");
return ret;
}
......@@ -266,8 +244,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
ctx, pwm_fan_groups);
if (IS_ERR(hwmon)) {
dev_err(&pdev->dev, "Failed to register hwmon device\n");
pwm_disable(ctx->pwm);
return PTR_ERR(hwmon);
ret = PTR_ERR(hwmon);
goto err_pwm_disable;
}
ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
......@@ -282,14 +260,20 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (IS_ERR(cdev)) {
dev_err(&pdev->dev,
"Failed to register pwm-fan as cooling device");
pwm_disable(ctx->pwm);
return PTR_ERR(cdev);
ret = PTR_ERR(cdev);
goto err_pwm_disable;
}
ctx->cdev = cdev;
thermal_cdev_update(cdev);
}
return 0;
err_pwm_disable:
state.enabled = false;
pwm_apply_state(ctx->pwm, &state);
return ret;
}
static int pwm_fan_remove(struct platform_device *pdev)
......
......@@ -16,6 +16,7 @@
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/scpi_protocol.h>
#include <linux/slab.h>
......@@ -23,6 +24,7 @@
#include <linux/thermal.h>
struct sensor_data {
unsigned int scale;
struct scpi_sensor_info info;
struct device_attribute dev_attr_input;
struct device_attribute dev_attr_label;
......@@ -44,6 +46,30 @@ struct scpi_sensors {
const struct attribute_group *groups[2];
};
static const u32 gxbb_scpi_scale[] = {
[TEMPERATURE] = 1, /* (celsius) */
[VOLTAGE] = 1000, /* (millivolts) */
[CURRENT] = 1000, /* (milliamperes) */
[POWER] = 1000000, /* (microwatts) */
[ENERGY] = 1000000, /* (microjoules) */
};
static const u32 scpi_scale[] = {
[TEMPERATURE] = 1000, /* (millicelsius) */
[VOLTAGE] = 1000, /* (millivolts) */
[CURRENT] = 1000, /* (milliamperes) */
[POWER] = 1000000, /* (microwatts) */
[ENERGY] = 1000000, /* (microjoules) */
};
static void scpi_scale_reading(u64 *value, struct sensor_data *sensor)
{
if (scpi_scale[sensor->info.class] != sensor->scale) {
*value *= scpi_scale[sensor->info.class];
do_div(*value, sensor->scale);
}
}
static int scpi_read_temp(void *dev, int *temp)
{
struct scpi_thermal_zone *zone = dev;
......@@ -57,6 +83,8 @@ static int scpi_read_temp(void *dev, int *temp)
if (ret)
return ret;
scpi_scale_reading(&value, sensor);
*temp = value;
return 0;
}
......@@ -77,6 +105,8 @@ scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
if (ret)
return ret;
scpi_scale_reading(&value, sensor);
return sprintf(buf, "%llu\n", value);
}
......@@ -94,14 +124,23 @@ static struct thermal_zone_of_device_ops scpi_sensor_ops = {
.get_temp = scpi_read_temp,
};
static const struct of_device_id scpi_of_match[] = {
{.compatible = "arm,scpi-sensors", .data = &scpi_scale},
{.compatible = "amlogic,meson-gxbb-scpi-sensors", .data = &gxbb_scpi_scale},
{},
};
MODULE_DEVICE_TABLE(of, scpi_of_match);
static int scpi_hwmon_probe(struct platform_device *pdev)
{
u16 nr_sensors, i;
const u32 *scale;
int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
int num_energy = 0;
struct scpi_ops *scpi_ops;
struct device *hwdev, *dev = &pdev->dev;
struct scpi_sensors *scpi_sensors;
const struct of_device_id *of_id;
int idx, ret;
scpi_ops = get_scpi_ops();
......@@ -131,6 +170,13 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
scpi_sensors->scpi_ops = scpi_ops;
of_id = of_match_device(scpi_of_match, &pdev->dev);
if (!of_id) {
dev_err(&pdev->dev, "Unable to initialize scpi-hwmon data\n");
return -ENODEV;
}
scale = of_id->data;
for (i = 0, idx = 0; i < nr_sensors; i++) {
struct sensor_data *sensor = &scpi_sensors->data[idx];
......@@ -178,6 +224,8 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
continue;
}
sensor->scale = scale[sensor->info.class];
sensor->dev_attr_input.attr.mode = S_IRUGO;
sensor->dev_attr_input.show = scpi_show_sensor;
sensor->dev_attr_input.attr.name = sensor->input;
......@@ -247,12 +295,6 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
return 0;
}
static const struct of_device_id scpi_of_match[] = {
{.compatible = "arm,scpi-sensors"},
{},
};
MODULE_DEVICE_TABLE(of, scpi_of_match);
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
.name = "scpi-hwmon",
......
......@@ -23,7 +23,7 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/i2c/ads1015.h>
#include <linux/platform_data/ads1015.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment