Commit 4d03390b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-v6.2-rc1' of...

Merge tag 'hwmon-for-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Driver for OneXPlayer mini AMD sensors

   - Ampere's Altra smpro-hwmon driver

  New chip and attribute support in existing drivers:

   - nct6775: Support for ASUS CROSSHAIR VIII/TUF/ProArt B550M

   - pmbus/ltc2978: Support for LTC7132

   - aquacomputer_d5next: Support for temperature sensor offsets and
     flow sensor pulses

   - coretemp: Support for dynamic ttarget and tjmax

  Improvements:

   - Use devm_regulator_get_enable() where appropriate

   - Use sysfs_emit() instead of scnprintf()

   - Remove some useless #include <linux/hwmon-vid.h>

   - Include <linux/kstrtox.h> when appropriate

   - Use simple i2c probe

   - it87: Check for a valid chip before using force_id, and new new
     module parameter to ignore ACPI resource conflicts

   - jc42: Use regmap, and restore min/max/critical temperatures on
     resume

   - Add reporting power good and status to PMBus based regulators

  Last minute fixes:

   - emc2305: Fix probing of emc2301/2/3, and fix setting pwm values
     manually if THERMAL is enabled

  And various other minor fixes and improvements"

* tag 'hwmon-for-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (37 commits)
  hwmon: (emc2305) fix pwm never being able to set lower
  hwmon: (emc2305) fix unable to probe emc2301/2/3
  hwmon: (dell-smm) Move error message to make probing silent
  hwmon: use sysfs_emit() to instead of scnprintf()
  hwmon: (oxp-sensors) Fix pwm reading
  hwmon: (aquacomputer_d5next) Add support for Quadro flow sensor pulses
  hwmon: (pmbus/core) Implement regulator get_status
  hwmon: (oxp-sensors) Add AOK ZOE and Mini PRO
  hwmon: (gsc-hwmon) Switch to flexible array to simplify code
  hwmon: (pmbus) Add power good support
  hwmon: (nct6775) add ASUS CROSSHAIR VIII/TUF/ProArt B550M
  hwmon: (coretemp) Add support for dynamic ttarget
  hwmon: (coretemp) Add support for dynamic tjmax
  hwmon: (coretemp) rearrange tjmax handing code
  hwmon: Remove some useless #include <linux/hwmon-vid.h>
  hwmon: (coretemp) Remove obsolete temp_data->valid
  hwmon: add OneXPlayer mini AMD sensors driver
  hwmon: (aquacomputer_d5next) Clear up macros and comments
  hwmon: (it87) Add DMI table for future extensions
  hwmon: Include <linux/kstrtox.h> when appropriate
  ...
parents 361c89a0 364ffd25
...@@ -39,7 +39,7 @@ current. ...@@ -39,7 +39,7 @@ current.
The Quadro exposes four physical and sixteen virtual temperature sensors, a flow The Quadro exposes four physical and sixteen virtual temperature sensors, a flow
sensor and four PWM controllable fans, along with their speed (in RPM), power, sensor and four PWM controllable fans, along with their speed (in RPM), power,
voltage and current. voltage and current. Flow sensor pulses are also available.
The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally, The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally,
sixteen virtual temperature sensors of the Farbwerk 360 are exposed. sixteen virtual temperature sensors of the Farbwerk 360 are exposed.
...@@ -62,7 +62,9 @@ Sysfs entries ...@@ -62,7 +62,9 @@ Sysfs entries
================ ============================================================== ================ ==============================================================
temp[1-20]_input Physical/virtual temperature sensors (in millidegrees Celsius) temp[1-20]_input Physical/virtual temperature sensors (in millidegrees Celsius)
temp[1-4]_offset Temperature sensor correction offset (in millidegrees Celsius)
fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h) fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h)
fan5_pulses Quadro flow sensor pulses
power[1-8]_input Pump/fan power (in micro Watts) power[1-8]_input Pump/fan power (in micro Watts)
in[0-7]_input Pump/fan voltage (in milli Volts) in[0-7]_input Pump/fan voltage (in milli Volts)
curr[1-8]_input Pump/fan current (in milli Amperes) curr[1-8]_input Pump/fan current (in milli Amperes)
......
...@@ -160,6 +160,7 @@ Hardware Monitoring Kernel Drivers ...@@ -160,6 +160,7 @@ Hardware Monitoring Kernel Drivers
nzxt-kraken2 nzxt-kraken2
nzxt-smart2 nzxt-smart2
occ occ
oxp-sensors
pc87360 pc87360
pc87427 pc87427
pcf8591 pcf8591
...@@ -187,6 +188,7 @@ Hardware Monitoring Kernel Drivers ...@@ -187,6 +188,7 @@ Hardware Monitoring Kernel Drivers
sis5595 sis5595
sl28cpld sl28cpld
smm665 smm665
smpro-hwmon
smsc47b397 smsc47b397
smsc47m192 smsc47m192
smsc47m1 smsc47m1
......
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver oxp-sensors
=========================
Author:
- Joaquín Ignacio Aramendía <samsagax@gmail.com>
Description:
------------
One X Player devices from One Netbook provide fan readings and fan control
through its Embedded Controller.
Currently only supports AMD boards from the One X Player and AOK ZOE lineup.
Intel boards could be supported if we could figure out the EC registers and
values to write to since the EC layout and model is different.
Supported devices
-----------------
Currently the driver supports the following handhelds:
- AOK ZOE A1
- OneXPlayer AMD
- OneXPlayer mini AMD
- OneXPlayer mini AMD PRO
Sysfs entries
-------------
The following attributes are supported:
fan1_input
Read Only. Reads current fan RMP.
pwm1_enable
Read Write. Enable manual fan control. Write "1" to set to manual, write "0"
to let the EC control de fan speed. Read this attribute to see current status.
pwm1
Read Write. Read this attribute to see current duty cycle in the range [0-255].
When pwm1_enable is set to "1" (manual) write any value in the range [0-255]
to set fan speed.
.. SPDX-License-Identifier: GPL-2.0-only
Kernel driver Ampere(R)'s Altra(R) SMpro hwmon
==============================================
Supported chips:
* Ampere(R) Altra(R)
Prefix: ``smpro``
Reference: `Altra SoC BMC Interface Specification`
Author: Thu Nguyen <thu@os.amperecomputing.com>
Description
-----------
The smpro-hwmon driver supports hardware monitoring for Ampere(R) Altra(R)
SoCs based on the SMpro co-processor (SMpro). The following sensor metrics
are supported by the driver:
* temperature
* voltage
* current
* power
The interface provides the registers to query the various sensors and
their values which are then exported to userspace by this driver.
Usage Notes
-----------
The driver creates at least two sysfs files for each sensor.
* ``<sensor_type><idx>_label`` reports the sensor label.
* ``<sensor_type><idx>_input`` returns the sensor value.
The sysfs files are allocated in the SMpro rootfs folder, with one root
directory for each instance.
When the SoC is turned off, the driver will fail to read registers and
return ``-ENXIO``.
Sysfs entries
-------------
The following sysfs files are supported:
* Ampere(R) Altra(R):
============ ============= ====== ===============================================
Name Unit Perm Description
============ ============= ====== ===============================================
temp1_input millicelsius RO SoC temperature
temp2_input millicelsius RO Max temperature reported among SoC VRDs
temp2_crit millicelsius RO SoC VRD HOT Threshold temperature
temp3_input millicelsius RO Max temperature reported among DIMM VRDs
temp4_input millicelsius RO Max temperature reported among Core VRDs
temp5_input millicelsius RO Temperature of DIMM0 on CH0
temp5_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp6_input millicelsius RO Temperature of DIMM0 on CH1
temp6_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp7_input millicelsius RO Temperature of DIMM0 on CH2
temp7_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp8_input millicelsius RO Temperature of DIMM0 on CH3
temp8_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp9_input millicelsius RO Temperature of DIMM0 on CH4
temp9_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp10_input millicelsius RO Temperature of DIMM0 on CH5
temp10_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp11_input millicelsius RO Temperature of DIMM0 on CH6
temp11_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp12_input millicelsius RO Temperature of DIMM0 on CH7
temp12_crit millicelsius RO MEM HOT Threshold for all DIMMs
temp13_input millicelsius RO Max temperature reported among RCA VRDs
in0_input millivolts RO Core voltage
in1_input millivolts RO SoC voltage
in2_input millivolts RO DIMM VRD1 voltage
in3_input millivolts RO DIMM VRD2 voltage
in4_input millivolts RO RCA VRD voltage
cur1_input milliamperes RO Core VRD current
cur2_input milliamperes RO SoC VRD current
cur3_input milliamperes RO DIMM VRD1 current
cur4_input milliamperes RO DIMM VRD2 current
cur5_input milliamperes RO RCA VRD current
power1_input microwatts RO Core VRD power
power2_input microwatts RO SoC VRD power
power3_input microwatts RO DIMM VRD1 power
power4_input microwatts RO DIMM VRD2 power
power5_input microwatts RO RCA VRD power
============ ============= ====== ===============================================
Example::
# cat in0_input
830
# cat temp1_input
37000
# cat curr1_input
9000
# cat power5_input
19500000
...@@ -15452,6 +15452,12 @@ S: Maintained ...@@ -15452,6 +15452,12 @@ S: Maintained
F: drivers/mtd/nand/onenand/ F: drivers/mtd/nand/onenand/
F: include/linux/mtd/onenand*.h F: include/linux/mtd/onenand*.h
ONEXPLAYER FAN DRIVER
M: Joaquín Ignacio Aramendía <samsagax@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/oxp-sensors.c
ONION OMEGA2+ BOARD ONION OMEGA2+ BOARD
M: Harvey Hunt <harveyhuntnexus@gmail.com> M: Harvey Hunt <harveyhuntnexus@gmail.com>
L: linux-mips@vger.kernel.org L: linux-mips@vger.kernel.org
......
...@@ -67,6 +67,14 @@ config SENSORS_ABITUGURU3 ...@@ -67,6 +67,14 @@ config SENSORS_ABITUGURU3
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called abituguru3. will be called abituguru3.
config SENSORS_SMPRO
tristate "Ampere's Altra SMpro hardware monitoring driver"
depends on MFD_SMPRO
help
If you say yes here you get support for the thermal, voltage,
current and power sensors of Ampere's Altra processor family SoC
with SMpro co-processor.
config SENSORS_AD7314 config SENSORS_AD7314
tristate "Analog Devices AD7314 and compatibles" tristate "Analog Devices AD7314 and compatibles"
depends on SPI depends on SPI
...@@ -799,6 +807,7 @@ config SENSORS_IT87 ...@@ -799,6 +807,7 @@ config SENSORS_IT87
config SENSORS_JC42 config SENSORS_JC42
tristate "JEDEC JC42.4 compliant memory module temperature sensors" tristate "JEDEC JC42.4 compliant memory module temperature sensors"
depends on I2C depends on I2C
select REGMAP_I2C
help help
If you say yes here, you get support for JEDEC JC42.4 compliant If you say yes here, you get support for JEDEC JC42.4 compliant
temperature sensors, which are used on many DDR3 memory modules for temperature sensors, which are used on many DDR3 memory modules for
...@@ -1607,6 +1616,17 @@ config SENSORS_NZXT_SMART2 ...@@ -1607,6 +1616,17 @@ config SENSORS_NZXT_SMART2
source "drivers/hwmon/occ/Kconfig" source "drivers/hwmon/occ/Kconfig"
config SENSORS_OXP
tristate "OneXPlayer EC fan control"
depends on ACPI
depends on X86
help
If you say yes here you get support for fan readings and control over
OneXPlayer handheld devices. Only OneXPlayer mini AMD handheld variant
boards are supported.
Can also be built as a module. In that case it will be called oxp-sensors.
config SENSORS_PCF8591 config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC" tristate "Philips PCF8591 ADC/DAC"
depends on I2C depends on I2C
......
...@@ -167,6 +167,7 @@ obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o ...@@ -167,6 +167,7 @@ obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o
obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o
obj-$(CONFIG_SENSORS_OXP) += oxp-sensors.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
...@@ -187,6 +188,7 @@ obj-$(CONFIG_SENSORS_SHT4x) += sht4x.o ...@@ -187,6 +188,7 @@ obj-$(CONFIG_SENSORS_SHT4x) += sht4x.o
obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMM665) += smm665.o obj-$(CONFIG_SENSORS_SMM665) += smm665.o
obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
......
...@@ -26,14 +26,12 @@ ...@@ -26,14 +26,12 @@
/** /**
* struct adm1177_state - driver instance specific data * struct adm1177_state - driver instance specific data
* @client: pointer to i2c client * @client: pointer to i2c client
* @reg: regulator info for the power supply of the device
* @r_sense_uohm: current sense resistor value * @r_sense_uohm: current sense resistor value
* @alert_threshold_ua: current limit for shutdown * @alert_threshold_ua: current limit for shutdown
* @vrange_high: internal voltage divider * @vrange_high: internal voltage divider
*/ */
struct adm1177_state { struct adm1177_state {
struct i2c_client *client; struct i2c_client *client;
struct regulator *reg;
u32 r_sense_uohm; u32 r_sense_uohm;
u32 alert_threshold_ua; u32 alert_threshold_ua;
bool vrange_high; bool vrange_high;
...@@ -189,13 +187,6 @@ static const struct hwmon_chip_info adm1177_chip_info = { ...@@ -189,13 +187,6 @@ static const struct hwmon_chip_info adm1177_chip_info = {
.info = adm1177_info, .info = adm1177_info,
}; };
static void adm1177_remove(void *data)
{
struct adm1177_state *st = data;
regulator_disable(st->reg);
}
static int adm1177_probe(struct i2c_client *client) static int adm1177_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
...@@ -210,22 +201,10 @@ static int adm1177_probe(struct i2c_client *client) ...@@ -210,22 +201,10 @@ static int adm1177_probe(struct i2c_client *client)
st->client = client; st->client = client;
st->reg = devm_regulator_get_optional(&client->dev, "vref"); ret = devm_regulator_get_enable_optional(&client->dev, "vref");
if (IS_ERR(st->reg)) { if (ret == -EPROBE_DEFER)
if (PTR_ERR(st->reg) == -EPROBE_DEFER)
return -EPROBE_DEFER; return -EPROBE_DEFER;
st->reg = NULL;
} else {
ret = regulator_enable(st->reg);
if (ret)
return ret;
ret = devm_add_action_or_reset(&client->dev, adm1177_remove,
st);
if (ret)
return ret;
}
if (device_property_read_u32(dev, "shunt-resistor-micro-ohms", if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",
&st->r_sense_uohm)) &st->r_sense_uohm))
st->r_sense_uohm = 0; st->r_sense_uohm = 0;
......
...@@ -289,8 +289,7 @@ static const struct hwmon_chip_info aht10_chip_info = { ...@@ -289,8 +289,7 @@ static const struct hwmon_chip_info aht10_chip_info = {
.info = aht10_info, .info = aht10_info,
}; };
static int aht10_probe(struct i2c_client *client, static int aht10_probe(struct i2c_client *client)
const struct i2c_device_id *aht10_id)
{ {
struct device *device = &client->dev; struct device *device = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
...@@ -336,7 +335,7 @@ static struct i2c_driver aht10_driver = { ...@@ -336,7 +335,7 @@ static struct i2c_driver aht10_driver = {
.driver = { .driver = {
.name = "aht10", .name = "aht10",
}, },
.probe = aht10_probe, .probe_new = aht10_probe,
.id_table = aht10_id, .id_table = aht10_id,
}; };
......
...@@ -59,7 +59,7 @@ static u8 secondary_ctrl_report[] = { ...@@ -59,7 +59,7 @@ static u8 secondary_ctrl_report[] = {
0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
}; };
/* Register offsets for all Aquacomputer devices */ /* Sensor sizes and offsets for all Aquacomputer devices */
#define AQC_TEMP_SENSOR_SIZE 0x02 #define AQC_TEMP_SENSOR_SIZE 0x02
#define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF #define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF
#define AQC_FAN_PERCENT_OFFSET 0x00 #define AQC_FAN_PERCENT_OFFSET 0x00
...@@ -68,62 +68,81 @@ static u8 secondary_ctrl_report[] = { ...@@ -68,62 +68,81 @@ static u8 secondary_ctrl_report[] = {
#define AQC_FAN_POWER_OFFSET 0x06 #define AQC_FAN_POWER_OFFSET 0x06
#define AQC_FAN_SPEED_OFFSET 0x08 #define AQC_FAN_SPEED_OFFSET 0x08
/* Register offsets for the D5 Next pump */ /* Specs of the D5 Next pump */
#define D5NEXT_POWER_CYCLES 0x18
#define D5NEXT_COOLANT_TEMP 0x57
#define D5NEXT_NUM_FANS 2 #define D5NEXT_NUM_FANS 2
#define D5NEXT_NUM_SENSORS 1 #define D5NEXT_NUM_SENSORS 1
#define D5NEXT_NUM_VIRTUAL_SENSORS 8 #define D5NEXT_NUM_VIRTUAL_SENSORS 8
#define D5NEXT_VIRTUAL_SENSORS_START 0x3f #define D5NEXT_CTRL_REPORT_SIZE 0x329
/* Sensor report offsets for the D5 Next pump */
#define D5NEXT_POWER_CYCLES 0x18
#define D5NEXT_COOLANT_TEMP 0x57
#define D5NEXT_PUMP_OFFSET 0x6c #define D5NEXT_PUMP_OFFSET 0x6c
#define D5NEXT_FAN_OFFSET 0x5f #define D5NEXT_FAN_OFFSET 0x5f
#define D5NEXT_5V_VOLTAGE 0x39 #define D5NEXT_5V_VOLTAGE 0x39
#define D5NEXT_12V_VOLTAGE 0x37 #define D5NEXT_12V_VOLTAGE 0x37
#define D5NEXT_CTRL_REPORT_SIZE 0x329 #define D5NEXT_VIRTUAL_SENSORS_START 0x3f
static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
/* Pump and fan speed registers in D5 Next control report (from 0-100%) */ /* Control report offsets for the D5 Next pump */
static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; #define D5NEXT_TEMP_CTRL_OFFSET 0x2D /* Temperature sensor offsets location */
static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */
/* Register offsets for the Farbwerk RGB controller */ /* Spec and sensor report offset for the Farbwerk RGB controller */
#define FARBWERK_NUM_SENSORS 4 #define FARBWERK_NUM_SENSORS 4
#define FARBWERK_SENSOR_START 0x2f #define FARBWERK_SENSOR_START 0x2f
/* Register offsets for the Farbwerk 360 RGB controller */ /* Specs of the Farbwerk 360 RGB controller */
#define FARBWERK360_NUM_SENSORS 4 #define FARBWERK360_NUM_SENSORS 4
#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_NUM_VIRTUAL_SENSORS 16 #define FARBWERK360_NUM_VIRTUAL_SENSORS 16
#define FARBWERK360_CTRL_REPORT_SIZE 0x682
/* Sensor report offsets for the Farbwerk 360 */
#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_VIRTUAL_SENSORS_START 0x3a #define FARBWERK360_VIRTUAL_SENSORS_START 0x3a
/* Register offsets for the Octo fan controller */ /* Control report offsets for the Farbwerk 360 */
#define OCTO_POWER_CYCLES 0x18 #define FARBWERK360_TEMP_CTRL_OFFSET 0x8
/* Specs of the Octo fan controller */
#define OCTO_NUM_FANS 8 #define OCTO_NUM_FANS 8
#define OCTO_NUM_SENSORS 4 #define OCTO_NUM_SENSORS 4
#define OCTO_SENSOR_START 0x3D
#define OCTO_NUM_VIRTUAL_SENSORS 16 #define OCTO_NUM_VIRTUAL_SENSORS 16
#define OCTO_VIRTUAL_SENSORS_START 0x45
#define OCTO_CTRL_REPORT_SIZE 0x65F #define OCTO_CTRL_REPORT_SIZE 0x65F
/* Sensor report offsets for the Octo */
#define OCTO_POWER_CYCLES 0x18
#define OCTO_SENSOR_START 0x3D
#define OCTO_VIRTUAL_SENSORS_START 0x45
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
/* Fan speed registers in Octo control report (from 0-100%) */ /* Control report offsets for the Octo */
#define OCTO_TEMP_CTRL_OFFSET 0xA
/* Fan speed offsets (0-100%) */
static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE }; static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
/* Register offsets for the Quadro fan controller */ /* Specs of Quadro fan controller */
#define QUADRO_POWER_CYCLES 0x18
#define QUADRO_NUM_FANS 4 #define QUADRO_NUM_FANS 4
#define QUADRO_NUM_SENSORS 4 #define QUADRO_NUM_SENSORS 4
#define QUADRO_SENSOR_START 0x34
#define QUADRO_NUM_VIRTUAL_SENSORS 16 #define QUADRO_NUM_VIRTUAL_SENSORS 16
#define QUADRO_VIRTUAL_SENSORS_START 0x3c
#define QUADRO_CTRL_REPORT_SIZE 0x3c1 #define QUADRO_CTRL_REPORT_SIZE 0x3c1
/* Sensor report offsets for the Quadro */
#define QUADRO_POWER_CYCLES 0x18
#define QUADRO_SENSOR_START 0x34
#define QUADRO_VIRTUAL_SENSORS_START 0x3c
#define QUADRO_FLOW_SENSOR_OFFSET 0x6e #define QUADRO_FLOW_SENSOR_OFFSET 0x6e
static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
/* Fan speed registers in Quadro control report (from 0-100%) */ /* Control report offsets for the Quadro */
static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; #define QUADRO_TEMP_CTRL_OFFSET 0xA
#define QUADRO_FLOW_PULSES_CTRL_OFFSET 0x6
static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed offsets (0-100%) */
/* Register offsets for the High Flow Next */ /* Specs of High Flow Next flow sensor */
#define HIGHFLOWNEXT_NUM_SENSORS 2 #define HIGHFLOWNEXT_NUM_SENSORS 2
/* Sensor report offsets for the High Flow Next */
#define HIGHFLOWNEXT_SENSOR_START 85 #define HIGHFLOWNEXT_SENSOR_START 85
#define HIGHFLOWNEXT_FLOW 81 #define HIGHFLOWNEXT_FLOW 81
#define HIGHFLOWNEXT_WATER_QUALITY 89 #define HIGHFLOWNEXT_WATER_QUALITY 89
...@@ -282,8 +301,10 @@ struct aqc_data { ...@@ -282,8 +301,10 @@ struct aqc_data {
int temp_sensor_start_offset; int temp_sensor_start_offset;
int num_virtual_temp_sensors; int num_virtual_temp_sensors;
int virtual_temp_sensor_start_offset; int virtual_temp_sensor_start_offset;
u16 temp_ctrl_offset;
u16 power_cycle_count_offset; u16 power_cycle_count_offset;
u8 flow_sensor_offset; u8 flow_sensor_offset;
u8 flow_pulses_ctrl_offset;
/* General info, same across all devices */ /* General info, same across all devices */
u32 serial_number[2]; u32 serial_number[2];
...@@ -365,8 +386,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv) ...@@ -365,8 +386,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv)
return ret; return ret;
} }
/* Refreshes the control buffer and returns value at offset */ /* Refreshes the control buffer and stores value at offset in val */
static int aqc_get_ctrl_val(struct aqc_data *priv, int offset) static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val)
{ {
int ret; int ret;
...@@ -376,7 +397,7 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset) ...@@ -376,7 +397,7 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
if (ret < 0) if (ret < 0)
goto unlock_and_return; goto unlock_and_return;
ret = get_unaligned_be16(priv->buffer + offset); *val = (s16)get_unaligned_be16(priv->buffer + offset);
unlock_and_return: unlock_and_return:
mutex_unlock(&priv->mutex); mutex_unlock(&priv->mutex);
...@@ -393,7 +414,7 @@ static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val) ...@@ -393,7 +414,7 @@ static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val)
if (ret < 0) if (ret < 0)
goto unlock_and_return; goto unlock_and_return;
put_unaligned_be16((u16)val, priv->buffer + offset); put_unaligned_be16((s16)val, priv->buffer + offset);
ret = aqc_send_ctrl_data(priv); ret = aqc_send_ctrl_data(priv);
...@@ -408,8 +429,28 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 ...@@ -408,8 +429,28 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
switch (type) { switch (type) {
case hwmon_temp: case hwmon_temp:
if (channel < priv->num_temp_sensors) {
switch (attr) {
case hwmon_temp_label:
case hwmon_temp_input:
return 0444;
case hwmon_temp_offset:
if (priv->temp_ctrl_offset != 0)
return 0644;
break;
default:
break;
}
}
if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors) if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors)
switch (attr) {
case hwmon_temp_label:
case hwmon_temp_input:
return 0444; return 0444;
default:
break;
}
break; break;
case hwmon_pwm: case hwmon_pwm:
if (priv->fan_ctrl_offsets && channel < priv->num_fans) { if (priv->fan_ctrl_offsets && channel < priv->num_fans) {
...@@ -422,9 +463,14 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 ...@@ -422,9 +463,14 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
} }
break; break;
case hwmon_fan: case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_label:
switch (priv->kind) { switch (priv->kind) {
case highflownext: case highflownext:
/* Special case to support flow sensor, water quality and conductivity */ /* Special case to support flow sensor, water quality
* and conductivity
*/
if (channel < 3) if (channel < 3)
return 0444; return 0444;
break; break;
...@@ -439,6 +485,15 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 ...@@ -439,6 +485,15 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
break; break;
} }
break; break;
case hwmon_fan_pulses:
/* Special case for Quadro flow sensor */
if (priv->kind == quadro && channel == priv->num_fans)
return 0644;
break;
default:
break;
}
break;
case hwmon_power: case hwmon_power:
switch (priv->kind) { switch (priv->kind) {
case highflownext: case highflownext:
...@@ -492,20 +547,46 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, ...@@ -492,20 +547,46 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
switch (type) { switch (type) {
case hwmon_temp: case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
if (priv->temp_input[channel] == -ENODATA) if (priv->temp_input[channel] == -ENODATA)
return -ENODATA; return -ENODATA;
*val = priv->temp_input[channel]; *val = priv->temp_input[channel];
break; break;
case hwmon_temp_offset:
ret =
aqc_get_ctrl_val(priv, priv->temp_ctrl_offset +
channel * AQC_TEMP_SENSOR_SIZE, val);
if (ret < 0)
return ret;
*val *= 10;
break;
default:
break;
}
break;
case hwmon_fan: case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
*val = priv->speed_input[channel]; *val = priv->speed_input[channel];
break; break;
case hwmon_fan_pulses:
ret = aqc_get_ctrl_val(priv, priv->flow_pulses_ctrl_offset, val);
if (ret < 0)
return ret;
break;
default:
break;
}
break;
case hwmon_power: case hwmon_power:
*val = priv->power_input[channel]; *val = priv->power_input[channel];
break; break;
case hwmon_pwm: case hwmon_pwm:
if (priv->fan_ctrl_offsets) { if (priv->fan_ctrl_offsets) {
ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel]); ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], val);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -563,6 +644,33 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, ...@@ -563,6 +644,33 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
struct aqc_data *priv = dev_get_drvdata(dev); struct aqc_data *priv = dev_get_drvdata(dev);
switch (type) { switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_offset:
/* Limit temp offset to +/- 15K as in the official software */
val = clamp_val(val, -15000, 15000) / 10;
ret =
aqc_set_ctrl_val(priv, priv->temp_ctrl_offset +
channel * AQC_TEMP_SENSOR_SIZE, val);
if (ret < 0)
return ret;
break;
default:
return -EOPNOTSUPP;
}
break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_pulses:
val = clamp_val(val, 10, 1000);
ret = aqc_set_ctrl_val(priv, priv->flow_pulses_ctrl_offset, val);
if (ret < 0)
return ret;
break;
default:
break;
}
break;
case hwmon_pwm: case hwmon_pwm:
switch (attr) { switch (attr) {
case hwmon_pwm_input: case hwmon_pwm_input:
...@@ -597,10 +705,10 @@ static const struct hwmon_ops aqc_hwmon_ops = { ...@@ -597,10 +705,10 @@ static const struct hwmon_ops aqc_hwmon_ops = {
static const struct hwmon_channel_info *aqc_info[] = { static const struct hwmon_channel_info *aqc_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
...@@ -622,7 +730,7 @@ static const struct hwmon_channel_info *aqc_info[] = { ...@@ -622,7 +730,7 @@ static const struct hwmon_channel_info *aqc_info[] = {
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_PULSES,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL), HWMON_F_INPUT | HWMON_F_LABEL),
...@@ -847,13 +955,17 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -847,13 +955,17 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->num_fans = D5NEXT_NUM_FANS; priv->num_fans = D5NEXT_NUM_FANS;
priv->fan_sensor_offsets = d5next_sensor_fan_offsets; priv->fan_sensor_offsets = d5next_sensor_fan_offsets;
priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets; priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
priv->num_temp_sensors = D5NEXT_NUM_SENSORS; priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS; priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START; priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START;
priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET;
priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE;
priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
priv->temp_label = label_d5next_temp; priv->temp_label = label_d5next_temp;
priv->virtual_temp_label = label_virtual_temp_sensors; priv->virtual_temp_label = label_virtual_temp_sensors;
priv->speed_label = label_d5next_speeds; priv->speed_label = label_d5next_speeds;
...@@ -865,18 +977,24 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -865,18 +977,24 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->kind = farbwerk; priv->kind = farbwerk;
priv->num_fans = 0; priv->num_fans = 0;
priv->num_temp_sensors = FARBWERK_NUM_SENSORS; priv->num_temp_sensors = FARBWERK_NUM_SENSORS;
priv->temp_sensor_start_offset = FARBWERK_SENSOR_START; priv->temp_sensor_start_offset = FARBWERK_SENSOR_START;
priv->temp_label = label_temp_sensors; priv->temp_label = label_temp_sensors;
break; break;
case USB_PRODUCT_ID_FARBWERK360: case USB_PRODUCT_ID_FARBWERK360:
priv->kind = farbwerk360; priv->kind = farbwerk360;
priv->num_fans = 0; priv->num_fans = 0;
priv->num_temp_sensors = FARBWERK360_NUM_SENSORS; priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START; priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS; priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START; priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START;
priv->temp_ctrl_offset = FARBWERK360_TEMP_CTRL_OFFSET;
priv->buffer_size = FARBWERK360_CTRL_REPORT_SIZE;
priv->temp_label = label_temp_sensors; priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors; priv->virtual_temp_label = label_virtual_temp_sensors;
...@@ -887,13 +1005,17 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -887,13 +1005,17 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->num_fans = OCTO_NUM_FANS; priv->num_fans = OCTO_NUM_FANS;
priv->fan_sensor_offsets = octo_sensor_fan_offsets; priv->fan_sensor_offsets = octo_sensor_fan_offsets;
priv->fan_ctrl_offsets = octo_ctrl_fan_offsets; priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
priv->num_temp_sensors = OCTO_NUM_SENSORS; priv->num_temp_sensors = OCTO_NUM_SENSORS;
priv->temp_sensor_start_offset = OCTO_SENSOR_START; priv->temp_sensor_start_offset = OCTO_SENSOR_START;
priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS; priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START; priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START;
priv->power_cycle_count_offset = OCTO_POWER_CYCLES; priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET;
priv->buffer_size = OCTO_CTRL_REPORT_SIZE; priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
priv->temp_label = label_temp_sensors; priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors; priv->virtual_temp_label = label_virtual_temp_sensors;
priv->speed_label = label_fan_speed; priv->speed_label = label_fan_speed;
...@@ -907,13 +1029,18 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -907,13 +1029,18 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->num_fans = QUADRO_NUM_FANS; priv->num_fans = QUADRO_NUM_FANS;
priv->fan_sensor_offsets = quadro_sensor_fan_offsets; priv->fan_sensor_offsets = quadro_sensor_fan_offsets;
priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets; priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
priv->num_temp_sensors = QUADRO_NUM_SENSORS; priv->num_temp_sensors = QUADRO_NUM_SENSORS;
priv->temp_sensor_start_offset = QUADRO_SENSOR_START; priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS; priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START; priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START;
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET;
priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET; priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET;
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
priv->temp_label = label_temp_sensors; priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors; priv->virtual_temp_label = label_virtual_temp_sensors;
...@@ -926,8 +1053,10 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -926,8 +1053,10 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->kind = highflownext; priv->kind = highflownext;
priv->num_fans = 0; priv->num_fans = 0;
priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS; priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS;
priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START; priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START;
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
priv->temp_label = label_highflownext_temp_sensors; priv->temp_label = label_highflownext_temp_sensors;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-vid.h> #include <linux/hwmon-vid.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/kstrtox.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/slab.h> #include <linux/slab.h>
......
...@@ -55,6 +55,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); ...@@ -55,6 +55,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
/* /*
* Per-Core Temperature Data * Per-Core Temperature Data
* @tjmax: The static tjmax value when tjmax cannot be retrieved from
* IA32_TEMPERATURE_TARGET MSR.
* @last_updated: The time when the current temperature value was updated * @last_updated: The time when the current temperature value was updated
* earlier (in jiffies). * earlier (in jiffies).
* @cpu_core_id: The CPU Core from which temperature values should be read * @cpu_core_id: The CPU Core from which temperature values should be read
...@@ -64,11 +66,9 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); ...@@ -64,11 +66,9 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
* @attr_size: Total number of pre-core attrs displayed in the sysfs. * @attr_size: Total number of pre-core attrs displayed in the sysfs.
* @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
* Otherwise, temp_data holds coretemp data. * Otherwise, temp_data holds coretemp data.
* @valid: If this is 1, the current temperature is valid.
*/ */
struct temp_data { struct temp_data {
int temp; int temp;
int ttarget;
int tjmax; int tjmax;
unsigned long last_updated; unsigned long last_updated;
unsigned int cpu; unsigned int cpu;
...@@ -76,7 +76,6 @@ struct temp_data { ...@@ -76,7 +76,6 @@ struct temp_data {
u32 status_reg; u32 status_reg;
int attr_size; int attr_size;
bool is_pkg_data; bool is_pkg_data;
bool valid;
struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
struct attribute *attrs[TOTAL_ATTRS + 1]; struct attribute *attrs[TOTAL_ATTRS + 1];
...@@ -95,85 +94,6 @@ struct platform_data { ...@@ -95,85 +94,6 @@ struct platform_data {
struct device_attribute name_attr; struct device_attribute name_attr;
}; };
/* Keep track of how many zone pointers we allocated in init() */
static int max_zones __read_mostly;
/* Array of zone pointers. Serialized by cpu hotplug lock */
static struct platform_device **zone_devices;
static ssize_t show_label(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
if (tdata->is_pkg_data)
return sprintf(buf, "Package id %u\n", pdata->pkg_id);
return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
}
static ssize_t show_crit_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u32 eax, edx;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
mutex_lock(&tdata->update_lock);
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
mutex_unlock(&tdata->update_lock);
return sprintf(buf, "%d\n", (eax >> 5) & 1);
}
static ssize_t show_tjmax(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax);
}
static ssize_t show_ttarget(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
}
static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u32 eax, edx;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
mutex_lock(&tdata->update_lock);
/* Check whether the time interval has elapsed */
if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) {
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
/*
* Ignore the valid bit. In all observed cases the register
* value is either low or zero if the valid bit is 0.
* Return it instead of reporting an error which doesn't
* really help at all.
*/
tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000;
tdata->valid = true;
tdata->last_updated = jiffies;
}
mutex_unlock(&tdata->update_lock);
return sprintf(buf, "%d\n", tdata->temp);
}
struct tjmax_pci { struct tjmax_pci {
unsigned int device; unsigned int device;
int tjmax; int tjmax;
...@@ -340,20 +260,25 @@ static bool cpu_has_tjmax(struct cpuinfo_x86 *c) ...@@ -340,20 +260,25 @@ static bool cpu_has_tjmax(struct cpuinfo_x86 *c)
model != 0x36; model != 0x36;
} }
static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) static int get_tjmax(struct temp_data *tdata, struct device *dev)
{ {
struct cpuinfo_x86 *c = &cpu_data(tdata->cpu);
int err; int err;
u32 eax, edx; u32 eax, edx;
u32 val; u32 val;
/* use static tjmax once it is set */
if (tdata->tjmax)
return tdata->tjmax;
/* /*
* A new feature of current Intel(R) processors, the * A new feature of current Intel(R) processors, the
* IA32_TEMPERATURE_TARGET contains the TjMax value * IA32_TEMPERATURE_TARGET contains the TjMax value
*/ */
err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); err = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (err) { if (err) {
if (cpu_has_tjmax(c)) if (cpu_has_tjmax(c))
dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); dev_warn(dev, "Unable to read TjMax from CPU %u\n", tdata->cpu);
} else { } else {
val = (eax >> 16) & 0xff; val = (eax >> 16) & 0xff;
/* /*
...@@ -369,14 +294,133 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) ...@@ -369,14 +294,133 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
if (force_tjmax) { if (force_tjmax) {
dev_notice(dev, "TjMax forced to %d degrees C by user\n", dev_notice(dev, "TjMax forced to %d degrees C by user\n",
force_tjmax); force_tjmax);
return force_tjmax * 1000; tdata->tjmax = force_tjmax * 1000;
} } else {
/* /*
* An assumption is made for early CPUs and unreadable MSR. * An assumption is made for early CPUs and unreadable MSR.
* NOTE: the calculated value may not be correct. * NOTE: the calculated value may not be correct.
*/ */
return adjust_tjmax(c, id, dev); tdata->tjmax = adjust_tjmax(c, tdata->cpu, dev);
}
return tdata->tjmax;
}
static int get_ttarget(struct temp_data *tdata, struct device *dev)
{
u32 eax, edx;
int tjmax, ttarget_offset, ret;
/*
* ttarget is valid only if tjmax can be retrieved from
* MSR_IA32_TEMPERATURE_TARGET
*/
if (tdata->tjmax)
return -ENODEV;
ret = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (ret)
return ret;
tjmax = (eax >> 16) & 0xff;
/* Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. */
ttarget_offset = (eax >> 8) & 0xff;
return (tjmax - ttarget_offset) * 1000;
}
/* Keep track of how many zone pointers we allocated in init() */
static int max_zones __read_mostly;
/* Array of zone pointers. Serialized by cpu hotplug lock */
static struct platform_device **zone_devices;
static ssize_t show_label(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
if (tdata->is_pkg_data)
return sprintf(buf, "Package id %u\n", pdata->pkg_id);
return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
}
static ssize_t show_crit_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u32 eax, edx;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
mutex_lock(&tdata->update_lock);
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
mutex_unlock(&tdata->update_lock);
return sprintf(buf, "%d\n", (eax >> 5) & 1);
}
static ssize_t show_tjmax(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
int tjmax;
mutex_lock(&tdata->update_lock);
tjmax = get_tjmax(tdata, dev);
mutex_unlock(&tdata->update_lock);
return sprintf(buf, "%d\n", tjmax);
}
static ssize_t show_ttarget(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
int ttarget;
mutex_lock(&tdata->update_lock);
ttarget = get_ttarget(tdata, dev);
mutex_unlock(&tdata->update_lock);
if (ttarget < 0)
return ttarget;
return sprintf(buf, "%d\n", ttarget);
}
static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u32 eax, edx;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
int tjmax;
mutex_lock(&tdata->update_lock);
tjmax = get_tjmax(tdata, dev);
/* Check whether the time interval has elapsed */
if (time_after(jiffies, tdata->last_updated + HZ)) {
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
/*
* Ignore the valid bit. In all observed cases the register
* value is either low or zero if the valid bit is 0.
* Return it instead of reporting an error which doesn't
* really help at all.
*/
tdata->temp = tjmax - ((eax >> 16) & 0x7f) * 1000;
tdata->last_updated = jiffies;
}
mutex_unlock(&tdata->update_lock);
return sprintf(buf, "%d\n", tdata->temp);
} }
static int create_core_attrs(struct temp_data *tdata, struct device *dev, static int create_core_attrs(struct temp_data *tdata, struct device *dev,
...@@ -490,23 +534,17 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, ...@@ -490,23 +534,17 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
if (err) if (err)
goto exit_free; goto exit_free;
/* We can access status register. Get Critical Temperature */ /* Make sure tdata->tjmax is a valid indicator for dynamic/static tjmax */
tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); get_tjmax(tdata, &pdev->dev);
/* /*
* Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. * The target temperature is available on older CPUs but not in the
* The target temperature is available on older CPUs but not in this * MSR_IA32_TEMPERATURE_TARGET register. Atoms don't have the register
* register. Atoms don't have the register at all. * at all.
*/ */
if (c->x86_model > 0xe && c->x86_model != 0x1c) { if (c->x86_model > 0xe && c->x86_model != 0x1c)
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, if (get_ttarget(tdata, &pdev->dev) >= 0)
&eax, &edx);
if (!err) {
tdata->ttarget
= tdata->tjmax - ((eax >> 8) & 0xff) * 1000;
tdata->attr_size++; tdata->attr_size++;
}
}
pdata->core_data[attr_no] = tdata; pdata->core_data[attr_no] = tdata;
......
...@@ -1447,9 +1447,10 @@ static int __init i8k_init(void) ...@@ -1447,9 +1447,10 @@ static int __init i8k_init(void)
*/ */
if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
pr_err("unable to get SMM Dell signature\n");
if (!force) if (!force)
return -ENODEV; return -ENODEV;
pr_err("Unable to get Dell SMM signature\n");
} }
dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL, dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL,
......
...@@ -269,7 +269,7 @@ static ssize_t update_interval_show(struct device *dev, ...@@ -269,7 +269,7 @@ static ssize_t update_interval_show(struct device *dev,
struct device_attribute *da, char *buf) struct device_attribute *da, char *buf)
{ {
struct ds1621_data *data = dev_get_drvdata(dev); struct ds1621_data *data = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%hu\n", data->update_interval); return sysfs_emit(buf, "%hu\n", data->update_interval);
} }
static ssize_t update_interval_store(struct device *dev, static ssize_t update_interval_store(struct device *dev,
......
...@@ -16,7 +16,6 @@ static const unsigned short ...@@ -16,7 +16,6 @@ static const unsigned short
emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END }; emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END };
#define EMC2305_REG_DRIVE_FAIL_STATUS 0x27 #define EMC2305_REG_DRIVE_FAIL_STATUS 0x27
#define EMC2305_REG_DEVICE 0xfd
#define EMC2305_REG_VENDOR 0xfe #define EMC2305_REG_VENDOR 0xfe
#define EMC2305_FAN_MAX 0xff #define EMC2305_FAN_MAX 0xff
#define EMC2305_FAN_MIN 0x00 #define EMC2305_FAN_MIN 0x00
...@@ -172,22 +171,12 @@ static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned l ...@@ -172,22 +171,12 @@ static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned l
return 0; return 0;
} }
static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) static int __emc2305_set_cur_state(struct emc2305_data *data, int cdev_idx, unsigned long state)
{ {
int cdev_idx, ret; int ret;
struct emc2305_data *data = cdev->devdata;
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
u8 val, i; u8 val, i;
if (state > data->max_state)
return -EINVAL;
cdev_idx = emc2305_get_cdev_idx(cdev);
if (cdev_idx < 0)
return cdev_idx;
/* Save thermal state. */
data->cdev_data[cdev_idx].last_thermal_state = state;
state = max_t(unsigned long, state, data->cdev_data[cdev_idx].last_hwmon_state); state = max_t(unsigned long, state, data->cdev_data[cdev_idx].last_hwmon_state);
val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX); val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX);
...@@ -212,6 +201,27 @@ static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned l ...@@ -212,6 +201,27 @@ static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned l
return 0; return 0;
} }
static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
int cdev_idx, ret;
struct emc2305_data *data = cdev->devdata;
if (state > data->max_state)
return -EINVAL;
cdev_idx = emc2305_get_cdev_idx(cdev);
if (cdev_idx < 0)
return cdev_idx;
/* Save thermal state. */
data->cdev_data[cdev_idx].last_thermal_state = state;
ret = __emc2305_set_cur_state(data, cdev_idx, state);
if (ret < 0)
return ret;
return 0;
}
static const struct thermal_cooling_device_ops emc2305_cooling_ops = { static const struct thermal_cooling_device_ops emc2305_cooling_ops = {
.get_max_state = emc2305_get_max_state, .get_max_state = emc2305_get_max_state,
.get_cur_state = emc2305_get_cur_state, .get_cur_state = emc2305_get_cur_state,
...@@ -402,7 +412,7 @@ emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int ch ...@@ -402,7 +412,7 @@ emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int ch
*/ */
if (data->cdev_data[cdev_idx].last_hwmon_state >= if (data->cdev_data[cdev_idx].last_hwmon_state >=
data->cdev_data[cdev_idx].last_thermal_state) data->cdev_data[cdev_idx].last_thermal_state)
return emc2305_set_cur_state(data->cdev_data[cdev_idx].cdev, return __emc2305_set_cur_state(data, cdev_idx,
data->cdev_data[cdev_idx].last_hwmon_state); data->cdev_data[cdev_idx].last_hwmon_state);
return 0; return 0;
} }
...@@ -518,13 +528,13 @@ static int emc2305_identify(struct device *dev) ...@@ -518,13 +528,13 @@ static int emc2305_identify(struct device *dev)
return 0; return 0;
} }
static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *id) static int emc2305_probe(struct i2c_client *client)
{ {
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct emc2305_data *data; struct emc2305_data *data;
struct emc2305_platform_data *pdata; struct emc2305_platform_data *pdata;
int vendor, device; int vendor;
int ret; int ret;
int i; int i;
...@@ -535,10 +545,6 @@ static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id * ...@@ -535,10 +545,6 @@ static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *
if (vendor != EMC2305_VENDOR) if (vendor != EMC2305_VENDOR)
return -ENODEV; return -ENODEV;
device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE);
if (device != EMC2305_DEVICE)
return -ENODEV;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
...@@ -607,7 +613,7 @@ static struct i2c_driver emc2305_driver = { ...@@ -607,7 +613,7 @@ static struct i2c_driver emc2305_driver = {
.driver = { .driver = {
.name = "emc2305", .name = "emc2305",
}, },
.probe = emc2305_probe, .probe_new = emc2305_probe,
.remove = emc2305_remove, .remove = emc2305_remove,
.id_table = emc2305_ids, .id_table = emc2305_ids,
.address_list = emc2305_normal_i2c, .address_list = emc2305_normal_i2c,
......
...@@ -1083,9 +1083,9 @@ static int fschmd_detect(struct i2c_client *client, ...@@ -1083,9 +1083,9 @@ static int fschmd_detect(struct i2c_client *client,
static int fschmd_probe(struct i2c_client *client) static int fschmd_probe(struct i2c_client *client)
{ {
struct fschmd_data *data; struct fschmd_data *data;
const char * const names[7] = { "Poseidon", "Hermes", "Scylla", static const char * const names[7] = { "Poseidon", "Hermes", "Scylla",
"Heracles", "Heimdall", "Hades", "Syleus" }; "Heracles", "Heimdall", "Hades", "Syleus" };
const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; static const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
int i, err; int i, err;
enum chips kind = i2c_match_id(fschmd_id, client)->driver_data; enum chips kind = i2c_match_id(fschmd_id, client)->driver_data;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/kstrtox.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
......
...@@ -257,13 +257,10 @@ gsc_hwmon_get_devtree_pdata(struct device *dev) ...@@ -257,13 +257,10 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
if (nchannels == 0) if (nchannels == 0)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
pdata = devm_kzalloc(dev, pdata = devm_kzalloc(dev, struct_size(pdata, channels, nchannels),
sizeof(*pdata) + nchannels * sizeof(*ch),
GFP_KERNEL); GFP_KERNEL);
if (!pdata) if (!pdata)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
ch = (struct gsc_hwmon_channel *)(pdata + 1);
pdata->channels = ch;
pdata->nchannels = nchannels; pdata->nchannels = nchannels;
/* fan controller base address */ /* fan controller base address */
...@@ -277,6 +274,7 @@ gsc_hwmon_get_devtree_pdata(struct device *dev) ...@@ -277,6 +274,7 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
of_node_put(fan); of_node_put(fan);
ch = pdata->channels;
/* allocate structures for channels and count instances of each type */ /* allocate structures for channels and count instances of each type */
device_for_each_child_node(dev, child) { device_for_each_child_node(dev, child) {
if (fwnode_property_read_string(child, "label", &ch->name)) { if (fwnode_property_read_string(child, "label", &ch->name)) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/kstrtox.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
......
...@@ -69,6 +69,10 @@ static unsigned short force_id; ...@@ -69,6 +69,10 @@ static unsigned short force_id;
module_param(force_id, ushort, 0); module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID"); MODULE_PARM_DESC(force_id, "Override the detected device ID");
static bool ignore_resource_conflict;
module_param(ignore_resource_conflict, bool, 0);
MODULE_PARM_DESC(ignore_resource_conflict, "Ignore ACPI resource conflict");
static struct platform_device *it87_pdev[2]; static struct platform_device *it87_pdev[2];
#define REG_2E 0x2e /* The register to read/write */ #define REG_2E 0x2e /* The register to read/write */
...@@ -563,6 +567,14 @@ struct it87_data { ...@@ -563,6 +567,14 @@ struct it87_data {
s8 auto_temp[NUM_AUTO_PWM][5]; /* [nr][0] is point1_temp_hyst */ s8 auto_temp[NUM_AUTO_PWM][5]; /* [nr][0] is point1_temp_hyst */
}; };
/* Board specific settings from DMI matching */
struct it87_dmi_data {
u8 skip_pwm; /* pwm channels to skip for this board */
};
/* Global for results from DMI matching, if needed */
static struct it87_dmi_data *dmi_data;
static int adc_lsb(const struct it87_data *data, int nr) static int adc_lsb(const struct it87_data *data, int nr)
{ {
int lsb; int lsb;
...@@ -2389,7 +2401,6 @@ static int __init it87_find(int sioaddr, unsigned short *address, ...@@ -2389,7 +2401,6 @@ static int __init it87_find(int sioaddr, unsigned short *address,
{ {
int err; int err;
u16 chip_type; u16 chip_type;
const char *board_vendor, *board_name;
const struct it87_devices *config; const struct it87_devices *config;
err = superio_enter(sioaddr); err = superio_enter(sioaddr);
...@@ -2397,7 +2408,13 @@ static int __init it87_find(int sioaddr, unsigned short *address, ...@@ -2397,7 +2408,13 @@ static int __init it87_find(int sioaddr, unsigned short *address,
return err; return err;
err = -ENODEV; err = -ENODEV;
chip_type = force_id ? force_id : superio_inw(sioaddr, DEVID); chip_type = superio_inw(sioaddr, DEVID);
/* check first for a valid chip before forcing chip id */
if (chip_type == 0xffff)
goto exit;
if (force_id)
chip_type = force_id;
switch (chip_type) { switch (chip_type) {
case IT8705F_DEVID: case IT8705F_DEVID:
...@@ -2802,24 +2819,9 @@ static int __init it87_find(int sioaddr, unsigned short *address, ...@@ -2802,24 +2819,9 @@ static int __init it87_find(int sioaddr, unsigned short *address,
if (sio_data->beep_pin) if (sio_data->beep_pin)
pr_info("Beeping is supported\n"); pr_info("Beeping is supported\n");
/* Disable specific features based on DMI strings */ /* Set values based on DMI matches */
board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); if (dmi_data)
board_name = dmi_get_system_info(DMI_BOARD_NAME); sio_data->skip_pwm |= dmi_data->skip_pwm;
if (board_vendor && board_name) {
if (strcmp(board_vendor, "nVIDIA") == 0 &&
strcmp(board_name, "FN68PT") == 0) {
/*
* On the Shuttle SN68PT, FAN_CTL2 is apparently not
* connected to a fan, but to something else. One user
* has reported instant system power-off when changing
* the PWM2 duty cycle, so we disable it.
* I use the board name string as the trigger in case
* the same board is ever used in other systems.
*/
pr_info("Disabling pwm2 due to hardware constraints\n");
sio_data->skip_pwm = BIT(1);
}
}
exit: exit:
superio_exit(sioaddr); superio_exit(sioaddr);
...@@ -3261,8 +3263,10 @@ static int __init it87_device_add(int index, unsigned short address, ...@@ -3261,8 +3263,10 @@ static int __init it87_device_add(int index, unsigned short address,
int err; int err;
err = acpi_check_resource_conflict(&res); err = acpi_check_resource_conflict(&res);
if (err) if (err) {
if (!ignore_resource_conflict)
return err; return err;
}
pdev = platform_device_alloc(DRVNAME, address); pdev = platform_device_alloc(DRVNAME, address);
if (!pdev) if (!pdev)
...@@ -3295,6 +3299,46 @@ static int __init it87_device_add(int index, unsigned short address, ...@@ -3295,6 +3299,46 @@ static int __init it87_device_add(int index, unsigned short address,
return err; return err;
} }
/* callback function for DMI */
static int it87_dmi_cb(const struct dmi_system_id *dmi_entry)
{
dmi_data = dmi_entry->driver_data;
if (dmi_data && dmi_data->skip_pwm)
pr_info("Disabling pwm2 due to hardware constraints\n");
return 1;
}
/*
* On the Shuttle SN68PT, FAN_CTL2 is apparently not
* connected to a fan, but to something else. One user
* has reported instant system power-off when changing
* the PWM2 duty cycle, so we disable it.
* I use the board name string as the trigger in case
* the same board is ever used in other systems.
*/
static struct it87_dmi_data nvidia_fn68pt = {
.skip_pwm = BIT(1),
};
#define IT87_DMI_MATCH_VND(vendor, name, cb, data) \
{ \
.callback = cb, \
.matches = { \
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, vendor), \
DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
}, \
.driver_data = data, \
}
static const struct dmi_system_id it87_dmi_table[] __initconst = {
IT87_DMI_MATCH_VND("nVIDIA", "FN68PT", it87_dmi_cb, &nvidia_fn68pt),
{ }
};
MODULE_DEVICE_TABLE(dmi, it87_dmi_table);
static int __init sm_it87_init(void) static int __init sm_it87_init(void)
{ {
int sioaddr[2] = { REG_2E, REG_4E }; int sioaddr[2] = { REG_2E, REG_4E };
...@@ -3307,6 +3351,8 @@ static int __init sm_it87_init(void) ...@@ -3307,6 +3351,8 @@ static int __init sm_it87_init(void)
if (err) if (err)
return err; return err;
dmi_check_system(it87_dmi_table);
for (i = 0; i < ARRAY_SIZE(sioaddr); i++) { for (i = 0; i < ARRAY_SIZE(sioaddr); i++) {
memset(&sio_data, 0, sizeof(struct it87_sio_data)); memset(&sio_data, 0, sizeof(struct it87_sio_data));
isa_address[i] = 0; isa_address[i] = 0;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/regmap.h>
/* Addresses to scan */ /* Addresses to scan */
static const unsigned short normal_i2c[] = { static const unsigned short normal_i2c[] = {
...@@ -36,20 +38,19 @@ static const unsigned short normal_i2c[] = { ...@@ -36,20 +38,19 @@ static const unsigned short normal_i2c[] = {
#define JC42_REG_SMBUS 0x22 /* NXP and Atmel, possibly others? */ #define JC42_REG_SMBUS 0x22 /* NXP and Atmel, possibly others? */
/* Status bits in temperature register */ /* Status bits in temperature register */
#define JC42_ALARM_CRIT_BIT 15 #define JC42_ALARM_CRIT BIT(15)
#define JC42_ALARM_MAX_BIT 14 #define JC42_ALARM_MAX BIT(14)
#define JC42_ALARM_MIN_BIT 13 #define JC42_ALARM_MIN BIT(13)
/* Configuration register defines */ /* Configuration register defines */
#define JC42_CFG_CRIT_ONLY (1 << 2) #define JC42_CFG_CRIT_ONLY BIT(2)
#define JC42_CFG_TCRIT_LOCK (1 << 6) #define JC42_CFG_TCRIT_LOCK BIT(6)
#define JC42_CFG_EVENT_LOCK (1 << 7) #define JC42_CFG_EVENT_LOCK BIT(7)
#define JC42_CFG_SHUTDOWN (1 << 8) #define JC42_CFG_SHUTDOWN BIT(8)
#define JC42_CFG_HYST_SHIFT 9 #define JC42_CFG_HYST_MASK GENMASK(10, 9)
#define JC42_CFG_HYST_MASK (0x03 << 9)
/* Capabilities */ /* Capabilities */
#define JC42_CAP_RANGE (1 << 2) #define JC42_CAP_RANGE BIT(2)
/* Manufacturer IDs */ /* Manufacturer IDs */
#define ADT_MANID 0x11d4 /* Analog Devices */ #define ADT_MANID 0x11d4 /* Analog Devices */
...@@ -199,31 +200,14 @@ static struct jc42_chips jc42_chips[] = { ...@@ -199,31 +200,14 @@ static struct jc42_chips jc42_chips[] = {
{ STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK }, { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK },
}; };
enum temp_index {
t_input = 0,
t_crit,
t_min,
t_max,
t_num_temp
};
static const u8 temp_regs[t_num_temp] = {
[t_input] = JC42_REG_TEMP,
[t_crit] = JC42_REG_TEMP_CRITICAL,
[t_min] = JC42_REG_TEMP_LOWER,
[t_max] = JC42_REG_TEMP_UPPER,
};
/* Each client has this additional data */ /* Each client has this additional data */
struct jc42_data { struct jc42_data {
struct i2c_client *client;
struct mutex update_lock; /* protect register access */ struct mutex update_lock; /* protect register access */
struct regmap *regmap;
bool extended; /* true if extended range supported */ bool extended; /* true if extended range supported */
bool valid; bool valid;
unsigned long last_updated; /* In jiffies */
u16 orig_config; /* original configuration */ u16 orig_config; /* original configuration */
u16 config; /* current configuration */ u16 config; /* current configuration */
u16 temp[t_num_temp];/* Temperatures */
}; };
#define JC42_TEMP_MIN_EXTENDED (-40000) #define JC42_TEMP_MIN_EXTENDED (-40000)
...@@ -248,85 +232,102 @@ static int jc42_temp_from_reg(s16 reg) ...@@ -248,85 +232,102 @@ static int jc42_temp_from_reg(s16 reg)
return reg * 125 / 2; return reg * 125 / 2;
} }
static struct jc42_data *jc42_update_device(struct device *dev)
{
struct jc42_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
struct jc42_data *ret = data;
int i, val;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
for (i = 0; i < t_num_temp; i++) {
val = i2c_smbus_read_word_swapped(client, temp_regs[i]);
if (val < 0) {
ret = ERR_PTR(val);
goto abort;
}
data->temp[i] = val;
}
data->last_updated = jiffies;
data->valid = true;
}
abort:
mutex_unlock(&data->update_lock);
return ret;
}
static int jc42_read(struct device *dev, enum hwmon_sensor_types type, static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val) u32 attr, int channel, long *val)
{ {
struct jc42_data *data = jc42_update_device(dev); struct jc42_data *data = dev_get_drvdata(dev);
int temp, hyst; unsigned int regval;
int ret, temp, hyst;
if (IS_ERR(data)) mutex_lock(&data->update_lock);
return PTR_ERR(data);
switch (attr) { switch (attr) {
case hwmon_temp_input: case hwmon_temp_input:
*val = jc42_temp_from_reg(data->temp[t_input]); ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
return 0; if (ret)
break;
*val = jc42_temp_from_reg(regval);
break;
case hwmon_temp_min: case hwmon_temp_min:
*val = jc42_temp_from_reg(data->temp[t_min]); ret = regmap_read(data->regmap, JC42_REG_TEMP_LOWER, &regval);
return 0; if (ret)
break;
*val = jc42_temp_from_reg(regval);
break;
case hwmon_temp_max: case hwmon_temp_max:
*val = jc42_temp_from_reg(data->temp[t_max]); ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
return 0; if (ret)
break;
*val = jc42_temp_from_reg(regval);
break;
case hwmon_temp_crit: case hwmon_temp_crit:
*val = jc42_temp_from_reg(data->temp[t_crit]); ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
return 0; &regval);
if (ret)
break;
*val = jc42_temp_from_reg(regval);
break;
case hwmon_temp_max_hyst: case hwmon_temp_max_hyst:
temp = jc42_temp_from_reg(data->temp[t_max]); ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) if (ret)
>> JC42_CFG_HYST_SHIFT]; break;
temp = jc42_temp_from_reg(regval);
hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
data->config)];
*val = temp - hyst; *val = temp - hyst;
return 0; break;
case hwmon_temp_crit_hyst: case hwmon_temp_crit_hyst:
temp = jc42_temp_from_reg(data->temp[t_crit]); ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) &regval);
>> JC42_CFG_HYST_SHIFT]; if (ret)
break;
temp = jc42_temp_from_reg(regval);
hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
data->config)];
*val = temp - hyst; *val = temp - hyst;
return 0; break;
case hwmon_temp_min_alarm: case hwmon_temp_min_alarm:
*val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1; ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
return 0; if (ret)
break;
*val = FIELD_GET(JC42_ALARM_MIN, regval);
break;
case hwmon_temp_max_alarm: case hwmon_temp_max_alarm:
*val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1; ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
return 0; if (ret)
break;
*val = FIELD_GET(JC42_ALARM_MAX, regval);
break;
case hwmon_temp_crit_alarm: case hwmon_temp_crit_alarm:
*val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1; ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
return 0; if (ret)
break;
*val = FIELD_GET(JC42_ALARM_CRIT, regval);
break;
default: default:
return -EOPNOTSUPP; ret = -EOPNOTSUPP;
break;
} }
mutex_unlock(&data->update_lock);
return ret;
} }
static int jc42_write(struct device *dev, enum hwmon_sensor_types type, static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val) u32 attr, int channel, long val)
{ {
struct jc42_data *data = dev_get_drvdata(dev); struct jc42_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; unsigned int regval;
int diff, hyst; int diff, hyst;
int ret; int ret;
...@@ -334,21 +335,23 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type, ...@@ -334,21 +335,23 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
switch (attr) { switch (attr) {
case hwmon_temp_min: case hwmon_temp_min:
data->temp[t_min] = jc42_temp_to_reg(val, data->extended); ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER,
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min], jc42_temp_to_reg(val, data->extended));
data->temp[t_min]);
break; break;
case hwmon_temp_max: case hwmon_temp_max:
data->temp[t_max] = jc42_temp_to_reg(val, data->extended); ret = regmap_write(data->regmap, JC42_REG_TEMP_UPPER,
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max], jc42_temp_to_reg(val, data->extended));
data->temp[t_max]);
break; break;
case hwmon_temp_crit: case hwmon_temp_crit:
data->temp[t_crit] = jc42_temp_to_reg(val, data->extended); ret = regmap_write(data->regmap, JC42_REG_TEMP_CRITICAL,
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit], jc42_temp_to_reg(val, data->extended));
data->temp[t_crit]);
break; break;
case hwmon_temp_crit_hyst: case hwmon_temp_crit_hyst:
ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
&regval);
if (ret)
break;
/* /*
* JC42.4 compliant chips only support four hysteresis values. * JC42.4 compliant chips only support four hysteresis values.
* Pick best choice and go from there. * Pick best choice and go from there.
...@@ -356,7 +359,7 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type, ...@@ -356,7 +359,7 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
: JC42_TEMP_MIN) - 6000, : JC42_TEMP_MIN) - 6000,
JC42_TEMP_MAX); JC42_TEMP_MAX);
diff = jc42_temp_from_reg(data->temp[t_crit]) - val; diff = jc42_temp_from_reg(regval) - val;
hyst = 0; hyst = 0;
if (diff > 0) { if (diff > 0) {
if (diff < 2250) if (diff < 2250)
...@@ -367,9 +370,8 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type, ...@@ -367,9 +370,8 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
hyst = 3; /* 6.0 degrees C */ hyst = 3; /* 6.0 degrees C */
} }
data->config = (data->config & ~JC42_CFG_HYST_MASK) | data->config = (data->config & ~JC42_CFG_HYST_MASK) |
(hyst << JC42_CFG_HYST_SHIFT); FIELD_PREP(JC42_CFG_HYST_MASK, hyst);
ret = i2c_smbus_write_word_swapped(data->client, ret = regmap_write(data->regmap, JC42_REG_CONFIG,
JC42_REG_CONFIG,
data->config); data->config);
break; break;
default: default:
...@@ -470,51 +472,80 @@ static const struct hwmon_chip_info jc42_chip_info = { ...@@ -470,51 +472,80 @@ static const struct hwmon_chip_info jc42_chip_info = {
.info = jc42_info, .info = jc42_info,
}; };
static bool jc42_readable_reg(struct device *dev, unsigned int reg)
{
return (reg >= JC42_REG_CAP && reg <= JC42_REG_DEVICEID) ||
reg == JC42_REG_SMBUS;
}
static bool jc42_writable_reg(struct device *dev, unsigned int reg)
{
return (reg >= JC42_REG_CONFIG && reg <= JC42_REG_TEMP_CRITICAL) ||
reg == JC42_REG_SMBUS;
}
static bool jc42_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == JC42_REG_CONFIG || reg == JC42_REG_TEMP;
}
static const struct regmap_config jc42_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.val_format_endian = REGMAP_ENDIAN_BIG,
.max_register = JC42_REG_SMBUS,
.writeable_reg = jc42_writable_reg,
.readable_reg = jc42_readable_reg,
.volatile_reg = jc42_volatile_reg,
.cache_type = REGCACHE_RBTREE,
};
static int jc42_probe(struct i2c_client *client) static int jc42_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
unsigned int config, cap;
struct jc42_data *data; struct jc42_data *data;
int config, cap; int ret;
data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = client; data->regmap = devm_regmap_init_i2c(client, &jc42_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
i2c_set_clientdata(client, data); i2c_set_clientdata(client, data);
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP); ret = regmap_read(data->regmap, JC42_REG_CAP, &cap);
if (cap < 0) if (ret)
return cap; return ret;
data->extended = !!(cap & JC42_CAP_RANGE); data->extended = !!(cap & JC42_CAP_RANGE);
if (device_property_read_bool(dev, "smbus-timeout-disable")) { if (device_property_read_bool(dev, "smbus-timeout-disable")) {
int smbus;
/* /*
* Not all chips support this register, but from a * Not all chips support this register, but from a
* quick read of various datasheets no chip appears * quick read of various datasheets no chip appears
* incompatible with the below attempt to disable * incompatible with the below attempt to disable
* the timeout. And the whole thing is opt-in... * the timeout. And the whole thing is opt-in...
*/ */
smbus = i2c_smbus_read_word_swapped(client, JC42_REG_SMBUS); ret = regmap_set_bits(data->regmap, JC42_REG_SMBUS,
if (smbus < 0) SMBUS_STMOUT);
return smbus; if (ret)
i2c_smbus_write_word_swapped(client, JC42_REG_SMBUS, return ret;
smbus | SMBUS_STMOUT);
} }
config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG); ret = regmap_read(data->regmap, JC42_REG_CONFIG, &config);
if (config < 0) if (ret)
return config; return ret;
data->orig_config = config; data->orig_config = config;
if (config & JC42_CFG_SHUTDOWN) { if (config & JC42_CFG_SHUTDOWN) {
config &= ~JC42_CFG_SHUTDOWN; config &= ~JC42_CFG_SHUTDOWN;
i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); regmap_write(data->regmap, JC42_REG_CONFIG, config);
} }
data->config = config; data->config = config;
...@@ -535,7 +566,7 @@ static void jc42_remove(struct i2c_client *client) ...@@ -535,7 +566,7 @@ static void jc42_remove(struct i2c_client *client)
config = (data->orig_config & ~JC42_CFG_HYST_MASK) config = (data->orig_config & ~JC42_CFG_HYST_MASK)
| (data->config & JC42_CFG_HYST_MASK); | (data->config & JC42_CFG_HYST_MASK);
i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); regmap_write(data->regmap, JC42_REG_CONFIG, config);
} }
} }
...@@ -546,8 +577,11 @@ static int jc42_suspend(struct device *dev) ...@@ -546,8 +577,11 @@ static int jc42_suspend(struct device *dev)
struct jc42_data *data = dev_get_drvdata(dev); struct jc42_data *data = dev_get_drvdata(dev);
data->config |= JC42_CFG_SHUTDOWN; data->config |= JC42_CFG_SHUTDOWN;
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
data->config);
regcache_cache_only(data->regmap, true);
regcache_mark_dirty(data->regmap);
return 0; return 0;
} }
...@@ -555,10 +589,13 @@ static int jc42_resume(struct device *dev) ...@@ -555,10 +589,13 @@ static int jc42_resume(struct device *dev)
{ {
struct jc42_data *data = dev_get_drvdata(dev); struct jc42_data *data = dev_get_drvdata(dev);
regcache_cache_only(data->regmap, false);
data->config &= ~JC42_CFG_SHUTDOWN; data->config &= ~JC42_CFG_SHUTDOWN;
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
data->config);
return 0; /* Restore cached register values to hardware */
return regcache_sync(data->regmap);
} }
static const struct dev_pm_ops jc42_dev_pm_ops = { static const struct dev_pm_ops jc42_dev_pm_ops = {
......
...@@ -92,7 +92,7 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *da, ...@@ -92,7 +92,7 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *da,
/* use integer division instead of equivalent right shift to /* use integer division instead of equivalent right shift to
guarantee arithmetic shift and preserve the sign */ guarantee arithmetic shift and preserve the sign */
temp = (((s16) err) * 250) / 32; temp = (((s16) err) * 250) / 32;
return scnprintf(buf, PAGE_SIZE, "%d\n", temp); return sysfs_emit(buf, "%d\n", temp);
} }
static ssize_t convrate_store(struct device *dev, struct device_attribute *da, static ssize_t convrate_store(struct device *dev, struct device_attribute *da,
...@@ -137,7 +137,7 @@ static ssize_t convrate_show(struct device *dev, struct device_attribute *da, ...@@ -137,7 +137,7 @@ static ssize_t convrate_show(struct device *dev, struct device_attribute *da,
int res; int res;
res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT; res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT;
return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]); return sysfs_emit(buf, "%hu\n", lm73_convrates[res]);
} }
static ssize_t maxmin_alarm_show(struct device *dev, static ssize_t maxmin_alarm_show(struct device *dev,
...@@ -154,7 +154,7 @@ static ssize_t maxmin_alarm_show(struct device *dev, ...@@ -154,7 +154,7 @@ static ssize_t maxmin_alarm_show(struct device *dev,
data->ctrl = ctrl; data->ctrl = ctrl;
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
return scnprintf(buf, PAGE_SIZE, "%d\n", (ctrl >> attr->index) & 1); return sysfs_emit(buf, "%d\n", (ctrl >> attr->index) & 1);
abort: abort:
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
......
...@@ -103,6 +103,7 @@ ...@@ -103,6 +103,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/kstrtox.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -2663,11 +2664,6 @@ static void lm90_remove_pec(void *dev) ...@@ -2663,11 +2664,6 @@ static void lm90_remove_pec(void *dev)
device_remove_file(dev, &dev_attr_pec); device_remove_file(dev, &dev_attr_pec);
} }
static void lm90_regulator_disable(void *regulator)
{
regulator_disable(regulator);
}
static int lm90_probe_channel_from_dt(struct i2c_client *client, static int lm90_probe_channel_from_dt(struct i2c_client *client,
struct device_node *child, struct device_node *child,
struct lm90_data *data) struct lm90_data *data)
...@@ -2749,24 +2745,13 @@ static int lm90_probe(struct i2c_client *client) ...@@ -2749,24 +2745,13 @@ static int lm90_probe(struct i2c_client *client)
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
struct hwmon_channel_info *info; struct hwmon_channel_info *info;
struct regulator *regulator;
struct device *hwmon_dev; struct device *hwmon_dev;
struct lm90_data *data; struct lm90_data *data;
int err; int err;
regulator = devm_regulator_get(dev, "vcc"); err = devm_regulator_get_enable(dev, "vcc");
if (IS_ERR(regulator))
return PTR_ERR(regulator);
err = regulator_enable(regulator);
if (err < 0) {
dev_err(dev, "Failed to enable regulator: %d\n", err);
return err;
}
err = devm_add_action_or_reset(dev, lm90_regulator_disable, regulator);
if (err) if (err)
return err; return dev_err_probe(dev, err, "Failed to enable regulator\n");
data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL);
if (!data) if (!data)
......
...@@ -881,7 +881,7 @@ static int ltc2992_parse_dt(struct ltc2992_state *st) ...@@ -881,7 +881,7 @@ static int ltc2992_parse_dt(struct ltc2992_state *st)
return 0; return 0;
} }
static int ltc2992_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) static int ltc2992_i2c_probe(struct i2c_client *client)
{ {
struct device *hwmon_dev; struct device *hwmon_dev;
struct ltc2992_state *st; struct ltc2992_state *st;
...@@ -927,7 +927,7 @@ static struct i2c_driver ltc2992_i2c_driver = { ...@@ -927,7 +927,7 @@ static struct i2c_driver ltc2992_i2c_driver = {
.name = "ltc2992", .name = "ltc2992",
.of_match_table = ltc2992_of_match, .of_match_table = ltc2992_of_match,
}, },
.probe = ltc2992_i2c_probe, .probe_new = ltc2992_i2c_probe,
.id_table = ltc2992_i2c_id, .id_table = ltc2992_i2c_id,
}; };
......
...@@ -303,8 +303,7 @@ static const struct hwmon_chip_info max127_chip_info = { ...@@ -303,8 +303,7 @@ static const struct hwmon_chip_info max127_chip_info = {
.info = max127_info, .info = max127_info,
}; };
static int max127_probe(struct i2c_client *client, static int max127_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
int i; int i;
struct device *hwmon_dev; struct device *hwmon_dev;
...@@ -340,7 +339,7 @@ static struct i2c_driver max127_driver = { ...@@ -340,7 +339,7 @@ static struct i2c_driver max127_driver = {
.driver = { .driver = {
.name = "max127", .name = "max127",
}, },
.probe = max127_probe, .probe_new = max127_probe,
.id_table = max127_id, .id_table = max127_id,
}; };
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/kstrtox.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/mutex.h> #include <linux/mutex.h>
......
...@@ -1043,7 +1043,9 @@ static struct platform_device *pdev[2]; ...@@ -1043,7 +1043,9 @@ static struct platform_device *pdev[2];
static const char * const asus_wmi_boards[] = { static const char * const asus_wmi_boards[] = {
"PRO H410T", "PRO H410T",
"ProArt B550-CREATOR",
"ProArt X570-CREATOR WIFI", "ProArt X570-CREATOR WIFI",
"ProArt Z490-CREATOR 10G",
"Pro B550M-C", "Pro B550M-C",
"Pro WS X570-ACE", "Pro WS X570-ACE",
"PRIME B360-PLUS", "PRIME B360-PLUS",
...@@ -1055,8 +1057,10 @@ static const char * const asus_wmi_boards[] = { ...@@ -1055,8 +1057,10 @@ static const char * const asus_wmi_boards[] = {
"PRIME X570-P", "PRIME X570-P",
"PRIME X570-PRO", "PRIME X570-PRO",
"ROG CROSSHAIR VIII DARK HERO", "ROG CROSSHAIR VIII DARK HERO",
"ROG CROSSHAIR VIII EXTREME",
"ROG CROSSHAIR VIII FORMULA", "ROG CROSSHAIR VIII FORMULA",
"ROG CROSSHAIR VIII HERO", "ROG CROSSHAIR VIII HERO",
"ROG CROSSHAIR VIII HERO (WI-FI)",
"ROG CROSSHAIR VIII IMPACT", "ROG CROSSHAIR VIII IMPACT",
"ROG STRIX B550-A GAMING", "ROG STRIX B550-A GAMING",
"ROG STRIX B550-E GAMING", "ROG STRIX B550-E GAMING",
...@@ -1080,8 +1084,11 @@ static const char * const asus_wmi_boards[] = { ...@@ -1080,8 +1084,11 @@ static const char * const asus_wmi_boards[] = {
"ROG STRIX Z490-G GAMING (WI-FI)", "ROG STRIX Z490-G GAMING (WI-FI)",
"ROG STRIX Z490-H GAMING", "ROG STRIX Z490-H GAMING",
"ROG STRIX Z490-I GAMING", "ROG STRIX Z490-I GAMING",
"TUF GAMING B550M-E",
"TUF GAMING B550M-E (WI-FI)",
"TUF GAMING B550M-PLUS", "TUF GAMING B550M-PLUS",
"TUF GAMING B550M-PLUS (WI-FI)", "TUF GAMING B550M-PLUS (WI-FI)",
"TUF GAMING B550M-PLUS WIFI II",
"TUF GAMING B550-PLUS", "TUF GAMING B550-PLUS",
"TUF GAMING B550-PLUS WIFI II", "TUF GAMING B550-PLUS WIFI II",
"TUF GAMING B550-PRO", "TUF GAMING B550-PRO",
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
config SENSORS_OCC_P8_I2C config SENSORS_OCC_P8_I2C
tristate "POWER8 OCC through I2C" tristate "POWER8 OCC through I2C"
depends on I2C depends on I2C
depends on ARM || ARM64 || COMPILE_TEST
select SENSORS_OCC select SENSORS_OCC
help help
This option enables support for monitoring sensors provided by the This option enables support for monitoring sensors provided by the
...@@ -21,7 +20,6 @@ config SENSORS_OCC_P8_I2C ...@@ -21,7 +20,6 @@ config SENSORS_OCC_P8_I2C
config SENSORS_OCC_P9_SBE config SENSORS_OCC_P9_SBE
tristate "POWER9 OCC through SBE" tristate "POWER9 OCC through SBE"
depends on FSI_OCC depends on FSI_OCC
depends on ARM || ARM64 || COMPILE_TEST
select SENSORS_OCC select SENSORS_OCC
help help
This option enables support for monitoring sensors provided by the This option enables support for monitoring sensors provided by the
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Platform driver for OXP Handhelds that expose fan reading and control
* via hwmon sysfs.
*
* Old boards have the same DMI strings and they are told appart by the
* boot cpu vendor (Intel/AMD). Currently only AMD boards are supported
* but the code is made to be simple to add other handheld boards in the
* future.
* Fan control is provided via pwm interface in the range [0-255].
* Old AMD boards use [0-100] as range in the EC, the written value is
* scaled to accommodate for that. Newer boards like the mini PRO and
* AOK ZOE are not scaled but have the same EC layout.
*
* Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/dev_printk.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/processor.h>
/* Handle ACPI lock mechanism */
static u32 oxp_mutex;
#define ACPI_LOCK_DELAY_MS 500
static bool lock_global_acpi_lock(void)
{
return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
}
static bool unlock_global_acpi_lock(void)
{
return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
}
enum oxp_board {
aok_zoe_a1 = 1,
oxp_mini_amd,
oxp_mini_amd_pro,
};
static enum oxp_board board;
#define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */
#define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */
#define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */
static const struct dmi_system_id dmi_table[] = {
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
},
.driver_data = (void *) &(enum oxp_board) {aok_zoe_a1},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
},
.driver_data = (void *) &(enum oxp_board) {oxp_mini_amd},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
},
.driver_data = (void *) &(enum oxp_board) {oxp_mini_amd_pro},
},
{},
};
/* Helper functions to handle EC read/write */
static int read_from_ec(u8 reg, int size, long *val)
{
int i;
int ret;
u8 buffer;
if (!lock_global_acpi_lock())
return -EBUSY;
*val = 0;
for (i = 0; i < size; i++) {
ret = ec_read(reg + i, &buffer);
if (ret)
return ret;
*val <<= i * 8;
*val += buffer;
}
if (!unlock_global_acpi_lock())
return -EBUSY;
return 0;
}
static int write_to_ec(const struct device *dev, u8 reg, u8 value)
{
int ret;
if (!lock_global_acpi_lock())
return -EBUSY;
ret = ec_write(reg, value);
if (!unlock_global_acpi_lock())
return -EBUSY;
return ret;
}
static int oxp_pwm_enable(const struct device *dev)
{
return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x01);
}
static int oxp_pwm_disable(const struct device *dev)
{
return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x00);
}
/* Callbacks for hwmon interface */
static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
enum hwmon_sensor_types type, u32 attr, int channel)
{
switch (type) {
case hwmon_fan:
return 0444;
case hwmon_pwm:
return 0644;
default:
return 0;
}
}
static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
int ret;
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
if (ret)
return ret;
if (board == oxp_mini_amd)
*val = (*val * 255) / 100;
return 0;
case hwmon_pwm_enable:
return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
switch (type) {
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_enable:
if (val == 1)
return oxp_pwm_enable(dev);
else if (val == 0)
return oxp_pwm_disable(dev);
return -EINVAL;
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
if (board == oxp_mini_amd)
val = (val * 100) / 255;
return write_to_ec(dev, OXP_SENSOR_PWM_REG, val);
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
/* Known sensors in the OXP EC controllers */
static const struct hwmon_channel_info *oxp_platform_sensors[] = {
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
NULL,
};
static const struct hwmon_ops oxp_ec_hwmon_ops = {
.is_visible = oxp_ec_hwmon_is_visible,
.read = oxp_platform_read,
.write = oxp_platform_write,
};
static const struct hwmon_chip_info oxp_ec_chip_info = {
.ops = &oxp_ec_hwmon_ops,
.info = oxp_platform_sensors,
};
/* Initialization logic */
static int oxp_platform_probe(struct platform_device *pdev)
{
const struct dmi_system_id *dmi_entry;
struct device *dev = &pdev->dev;
struct device *hwdev;
/*
* Have to check for AMD processor here because DMI strings are the
* same between Intel and AMD boards, the only way to tell them appart
* is the CPU.
* Intel boards seem to have different EC registers and values to
* read/write.
*/
dmi_entry = dmi_first_match(dmi_table);
if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return -ENODEV;
board = *((enum oxp_board *) dmi_entry->driver_data);
hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
&oxp_ec_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwdev);
}
static struct platform_driver oxp_platform_driver = {
.driver = {
.name = "oxp-platform",
},
.probe = oxp_platform_probe,
};
static struct platform_device *oxp_platform_device;
static int __init oxp_platform_init(void)
{
oxp_platform_device =
platform_create_bundle(&oxp_platform_driver,
oxp_platform_probe, NULL, 0, NULL, 0);
return PTR_ERR_OR_ZERO(oxp_platform_device);
}
static void __exit oxp_platform_exit(void)
{
platform_device_unregister(oxp_platform_device);
platform_driver_unregister(&oxp_platform_driver);
}
MODULE_DEVICE_TABLE(dmi, dmi_table);
module_init(oxp_platform_init);
module_exit(oxp_platform_exit);
MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
MODULE_LICENSE("GPL");
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/kstrtox.h>
/* Insmod parameters */ /* Insmod parameters */
......
...@@ -23,7 +23,7 @@ enum chips { ...@@ -23,7 +23,7 @@ enum chips {
/* Managers */ /* Managers */
ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
/* Controllers */ /* Controllers */
ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7880, ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880,
/* Modules */ /* Modules */
ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
ltm4700, ltm4700,
...@@ -45,15 +45,14 @@ enum chips { ...@@ -45,15 +45,14 @@ enum chips {
#define LTC2974_MFR_IOUT_PEAK 0xd7 #define LTC2974_MFR_IOUT_PEAK 0xd7
#define LTC2974_MFR_IOUT_MIN 0xd8 #define LTC2974_MFR_IOUT_MIN 0xd8
/* LTC3880, LTC3882, LTC3883, LTC3887, LTM4675, and LTM4676 */ /* LTC3880, LTC3882, LTC3883, LTC3887, LTM4675, LTM4676, LTC7132 */
#define LTC3880_MFR_IOUT_PEAK 0xd7 #define LTC3880_MFR_IOUT_PEAK 0xd7
#define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_CLEAR_PEAKS 0xe3
#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
/* LTC3883, LTC3884, LTC3886, LTC3889 and LTC7880 only */ /* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7880 */
#define LTC3883_MFR_IIN_PEAK 0xe1 #define LTC3883_MFR_IIN_PEAK 0xe1
/* LTC2975 only */ /* LTC2975 only */
#define LTC2975_MFR_IIN_PEAK 0xc4 #define LTC2975_MFR_IIN_PEAK 0xc4
#define LTC2975_MFR_IIN_MIN 0xc5 #define LTC2975_MFR_IIN_MIN 0xc5
...@@ -79,10 +78,11 @@ enum chips { ...@@ -79,10 +78,11 @@ enum chips {
#define LTC3884_ID 0x4C00 #define LTC3884_ID 0x4C00
#define LTC3886_ID 0x4600 #define LTC3886_ID 0x4600
#define LTC3887_ID 0x4700 #define LTC3887_ID 0x4700
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
#define LTM2987_ID_B 0x8020
#define LTC3889_ID 0x4900 #define LTC3889_ID 0x4900
#define LTC7132_ID 0x4CE0
#define LTC7880_ID 0x49E0 #define LTC7880_ID 0x49E0
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
#define LTM2987_ID_B 0x8020
#define LTM4664_ID 0x4120 #define LTM4664_ID 0x4120
#define LTM4675_ID 0x47a0 #define LTM4675_ID 0x47a0
#define LTM4676_ID_REV1 0x4400 #define LTM4676_ID_REV1 0x4400
...@@ -547,6 +547,7 @@ static const struct i2c_device_id ltc2978_id[] = { ...@@ -547,6 +547,7 @@ static const struct i2c_device_id ltc2978_id[] = {
{"ltc3886", ltc3886}, {"ltc3886", ltc3886},
{"ltc3887", ltc3887}, {"ltc3887", ltc3887},
{"ltc3889", ltc3889}, {"ltc3889", ltc3889},
{"ltc7132", ltc7132},
{"ltc7880", ltc7880}, {"ltc7880", ltc7880},
{"ltm2987", ltm2987}, {"ltm2987", ltm2987},
{"ltm4664", ltm4664}, {"ltm4664", ltm4664},
...@@ -651,6 +652,8 @@ static int ltc2978_get_id(struct i2c_client *client) ...@@ -651,6 +652,8 @@ static int ltc2978_get_id(struct i2c_client *client)
return ltc3887; return ltc3887;
else if (chip_id == LTC3889_ID) else if (chip_id == LTC3889_ID)
return ltc3889; return ltc3889;
else if (chip_id == LTC7132_ID)
return ltc7132;
else if (chip_id == LTC7880_ID) else if (chip_id == LTC7880_ID)
return ltc7880; return ltc7880;
else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B) else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B)
...@@ -831,6 +834,7 @@ static int ltc2978_probe(struct i2c_client *client) ...@@ -831,6 +834,7 @@ static int ltc2978_probe(struct i2c_client *client)
case ltc3884: case ltc3884:
case ltc3886: case ltc3886:
case ltc3889: case ltc3889:
case ltc7132:
case ltc7880: case ltc7880:
case ltm4664: case ltm4664:
case ltm4678: case ltm4678:
...@@ -902,6 +906,7 @@ static const struct of_device_id ltc2978_of_match[] = { ...@@ -902,6 +906,7 @@ static const struct of_device_id ltc2978_of_match[] = {
{ .compatible = "lltc,ltc3886" }, { .compatible = "lltc,ltc3886" },
{ .compatible = "lltc,ltc3887" }, { .compatible = "lltc,ltc3887" },
{ .compatible = "lltc,ltc3889" }, { .compatible = "lltc,ltc3889" },
{ .compatible = "lltc,ltc7132" },
{ .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltc7880" },
{ .compatible = "lltc,ltm2987" }, { .compatible = "lltc,ltm2987" },
{ .compatible = "lltc,ltm4664" }, { .compatible = "lltc,ltm4664" },
......
...@@ -2827,9 +2827,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned ...@@ -2827,9 +2827,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
if (status < 0) if (status < 0)
return status; return status;
if (pmbus_regulator_is_enabled(rdev) && (status & PB_STATUS_OFF)) if (pmbus_regulator_is_enabled(rdev)) {
if (status & PB_STATUS_OFF)
*flags |= REGULATOR_ERROR_FAIL; *flags |= REGULATOR_ERROR_FAIL;
if (status & PB_STATUS_POWER_GOOD_N)
*flags |= REGULATOR_ERROR_REGULATION_OUT;
}
/* /*
* Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are * Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are
* defined strictly as fault indicators (not warnings). * defined strictly as fault indicators (not warnings).
...@@ -2851,6 +2855,49 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned ...@@ -2851,6 +2855,49 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
return 0; return 0;
} }
static int pmbus_regulator_get_status(struct regulator_dev *rdev)
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
u8 page = rdev_get_id(rdev);
int status, ret;
mutex_lock(&data->update_lock);
status = pmbus_get_status(client, page, PMBUS_STATUS_WORD);
if (status < 0) {
ret = status;
goto unlock;
}
if (status & PB_STATUS_OFF) {
ret = REGULATOR_STATUS_OFF;
goto unlock;
}
/* If regulator is ON & reports power good then return ON */
if (!(status & PB_STATUS_POWER_GOOD_N)) {
ret = REGULATOR_STATUS_ON;
goto unlock;
}
ret = pmbus_regulator_get_error_flags(rdev, &status);
if (ret)
goto unlock;
if (status & (REGULATOR_ERROR_UNDER_VOLTAGE | REGULATOR_ERROR_OVER_CURRENT |
REGULATOR_ERROR_REGULATION_OUT | REGULATOR_ERROR_FAIL | REGULATOR_ERROR_OVER_TEMP)) {
ret = REGULATOR_STATUS_ERROR;
goto unlock;
}
ret = REGULATOR_STATUS_UNDEFINED;
unlock:
mutex_unlock(&data->update_lock);
return ret;
}
static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page) static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page)
{ {
struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_data *data = i2c_get_clientdata(client);
...@@ -2991,6 +3038,7 @@ const struct regulator_ops pmbus_regulator_ops = { ...@@ -2991,6 +3038,7 @@ const struct regulator_ops pmbus_regulator_ops = {
.disable = pmbus_regulator_disable, .disable = pmbus_regulator_disable,
.is_enabled = pmbus_regulator_is_enabled, .is_enabled = pmbus_regulator_is_enabled,
.get_error_flags = pmbus_regulator_get_error_flags, .get_error_flags = pmbus_regulator_get_error_flags,
.get_status = pmbus_regulator_get_status,
.get_voltage = pmbus_regulator_get_voltage, .get_voltage = pmbus_regulator_get_voltage,
.set_voltage = pmbus_regulator_set_voltage, .set_voltage = pmbus_regulator_set_voltage,
.list_voltage = pmbus_regulator_list_voltage, .list_voltage = pmbus_regulator_list_voltage,
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/kstrtox.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include "pmbus.h" #include "pmbus.h"
......
...@@ -297,8 +297,7 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) ...@@ -297,8 +297,7 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
return ret; return ret;
} }
static int sbrmi_probe(struct i2c_client *client, static int sbrmi_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
...@@ -348,7 +347,7 @@ static struct i2c_driver sbrmi_driver = { ...@@ -348,7 +347,7 @@ static struct i2c_driver sbrmi_driver = {
.name = "sbrmi", .name = "sbrmi",
.of_match_table = of_match_ptr(sbrmi_of_match), .of_match_table = of_match_ptr(sbrmi_of_match),
}, },
.probe = sbrmi_probe, .probe_new = sbrmi_probe,
.id_table = sbrmi_id, .id_table = sbrmi_id,
}; };
......
...@@ -199,8 +199,7 @@ static const struct hwmon_chip_info sbtsi_chip_info = { ...@@ -199,8 +199,7 @@ static const struct hwmon_chip_info sbtsi_chip_info = {
.info = sbtsi_info, .info = sbtsi_info,
}; };
static int sbtsi_probe(struct i2c_client *client, static int sbtsi_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
...@@ -239,7 +238,7 @@ static struct i2c_driver sbtsi_driver = { ...@@ -239,7 +238,7 @@ static struct i2c_driver sbtsi_driver = {
.name = "sbtsi", .name = "sbtsi",
.of_match_table = of_match_ptr(sbtsi_of_match), .of_match_table = of_match_ptr(sbtsi_of_match),
}, },
.probe = sbtsi_probe, .probe_new = sbtsi_probe,
.id_table = sbtsi_id, .id_table = sbtsi_id,
}; };
......
...@@ -320,7 +320,7 @@ static ssize_t temp1_limit_show(struct device *dev, ...@@ -320,7 +320,7 @@ static ssize_t temp1_limit_show(struct device *dev,
u8 index = to_sensor_dev_attr(attr)->index; u8 index = to_sensor_dev_attr(attr)->index;
int temperature_limit = data->temperature_limits[index]; int temperature_limit = data->temperature_limits[index];
return scnprintf(buf, PAGE_SIZE, "%d\n", temperature_limit); return sysfs_emit(buf, "%d\n", temperature_limit);
} }
static ssize_t humidity1_limit_show(struct device *dev, static ssize_t humidity1_limit_show(struct device *dev,
...@@ -331,7 +331,7 @@ static ssize_t humidity1_limit_show(struct device *dev, ...@@ -331,7 +331,7 @@ static ssize_t humidity1_limit_show(struct device *dev,
u8 index = to_sensor_dev_attr(attr)->index; u8 index = to_sensor_dev_attr(attr)->index;
u32 humidity_limit = data->humidity_limits[index]; u32 humidity_limit = data->humidity_limits[index];
return scnprintf(buf, PAGE_SIZE, "%u\n", humidity_limit); return sysfs_emit(buf, "%u\n", humidity_limit);
} }
/* /*
...@@ -483,7 +483,7 @@ static ssize_t temp1_alarm_show(struct device *dev, ...@@ -483,7 +483,7 @@ static ssize_t temp1_alarm_show(struct device *dev,
if (ret) if (ret)
return ret; return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x04)); return sysfs_emit(buf, "%d\n", !!(buffer[0] & 0x04));
} }
static ssize_t humidity1_alarm_show(struct device *dev, static ssize_t humidity1_alarm_show(struct device *dev,
...@@ -498,7 +498,7 @@ static ssize_t humidity1_alarm_show(struct device *dev, ...@@ -498,7 +498,7 @@ static ssize_t humidity1_alarm_show(struct device *dev,
if (ret) if (ret)
return ret; return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x08)); return sysfs_emit(buf, "%d\n", !!(buffer[0] & 0x08));
} }
static ssize_t heater_enable_show(struct device *dev, static ssize_t heater_enable_show(struct device *dev,
...@@ -513,7 +513,7 @@ static ssize_t heater_enable_show(struct device *dev, ...@@ -513,7 +513,7 @@ static ssize_t heater_enable_show(struct device *dev,
if (ret) if (ret)
return ret; return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x20)); return sysfs_emit(buf, "%d\n", !!(buffer[0] & 0x20));
} }
static ssize_t heater_enable_store(struct device *dev, static ssize_t heater_enable_store(struct device *dev,
...@@ -550,7 +550,7 @@ static ssize_t update_interval_show(struct device *dev, ...@@ -550,7 +550,7 @@ static ssize_t update_interval_show(struct device *dev,
{ {
struct sht3x_data *data = dev_get_drvdata(dev); struct sht3x_data *data = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", return sysfs_emit(buf, "%u\n",
mode_to_update_interval[data->mode]); mode_to_update_interval[data->mode]);
} }
......
...@@ -232,8 +232,7 @@ static const struct hwmon_chip_info sht4x_chip_info = { ...@@ -232,8 +232,7 @@ static const struct hwmon_chip_info sht4x_chip_info = {
.info = sht4x_info, .info = sht4x_info,
}; };
static int sht4x_probe(struct i2c_client *client, static int sht4x_probe(struct i2c_client *client)
const struct i2c_device_id *sht4x_id)
{ {
struct device *device = &client->dev; struct device *device = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
...@@ -292,7 +291,7 @@ static struct i2c_driver sht4x_driver = { ...@@ -292,7 +291,7 @@ static struct i2c_driver sht4x_driver = {
.name = "sht4x", .name = "sht4x",
.of_match_table = sht4x_of_match, .of_match_table = sht4x_of_match,
}, },
.probe = sht4x_probe, .probe_new = sht4x_probe,
.id_table = sht4x_id, .id_table = sht4x_id,
}; };
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ampere Computing SoC's SMPro Hardware Monitoring Driver
*
* Copyright (c) 2022, Ampere Computing LLC
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
/* Logical Power Sensor Registers */
#define SOC_TEMP 0x10
#define SOC_VRD_TEMP 0x11
#define DIMM_VRD_TEMP 0x12
#define CORE_VRD_TEMP 0x13
#define CH0_DIMM_TEMP 0x14
#define CH1_DIMM_TEMP 0x15
#define CH2_DIMM_TEMP 0x16
#define CH3_DIMM_TEMP 0x17
#define CH4_DIMM_TEMP 0x18
#define CH5_DIMM_TEMP 0x19
#define CH6_DIMM_TEMP 0x1A
#define CH7_DIMM_TEMP 0x1B
#define RCA_VRD_TEMP 0x1C
#define CORE_VRD_PWR 0x20
#define SOC_PWR 0x21
#define DIMM_VRD1_PWR 0x22
#define DIMM_VRD2_PWR 0x23
#define CORE_VRD_PWR_MW 0x26
#define SOC_PWR_MW 0x27
#define DIMM_VRD1_PWR_MW 0x28
#define DIMM_VRD2_PWR_MW 0x29
#define RCA_VRD_PWR 0x2A
#define RCA_VRD_PWR_MW 0x2B
#define MEM_HOT_THRESHOLD 0x32
#define SOC_VR_HOT_THRESHOLD 0x33
#define CORE_VRD_VOLT 0x34
#define SOC_VRD_VOLT 0x35
#define DIMM_VRD1_VOLT 0x36
#define DIMM_VRD2_VOLT 0x37
#define RCA_VRD_VOLT 0x38
#define CORE_VRD_CURR 0x39
#define SOC_VRD_CURR 0x3A
#define DIMM_VRD1_CURR 0x3B
#define DIMM_VRD2_CURR 0x3C
#define RCA_VRD_CURR 0x3D
struct smpro_hwmon {
struct regmap *regmap;
};
struct smpro_sensor {
const u8 reg;
const u8 reg_ext;
const char *label;
};
static const struct smpro_sensor temperature[] = {
{
.reg = SOC_TEMP,
.label = "temp1 SoC"
},
{
.reg = SOC_VRD_TEMP,
.reg_ext = SOC_VR_HOT_THRESHOLD,
.label = "temp2 SoC VRD"
},
{
.reg = DIMM_VRD_TEMP,
.label = "temp3 DIMM VRD"
},
{
.reg = CORE_VRD_TEMP,
.label = "temp4 CORE VRD"
},
{
.reg = CH0_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp5 CH0 DIMM"
},
{
.reg = CH1_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp6 CH1 DIMM"
},
{
.reg = CH2_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp7 CH2 DIMM"
},
{
.reg = CH3_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp8 CH3 DIMM"
},
{
.reg = CH4_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp9 CH4 DIMM"
},
{
.reg = CH5_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp10 CH5 DIMM"
},
{
.reg = CH6_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp11 CH6 DIMM"
},
{
.reg = CH7_DIMM_TEMP,
.reg_ext = MEM_HOT_THRESHOLD,
.label = "temp12 CH7 DIMM"
},
{
.reg = RCA_VRD_TEMP,
.label = "temp13 RCA VRD"
},
};
static const struct smpro_sensor voltage[] = {
{
.reg = CORE_VRD_VOLT,
.label = "vout0 CORE VRD"
},
{
.reg = SOC_VRD_VOLT,
.label = "vout1 SoC VRD"
},
{
.reg = DIMM_VRD1_VOLT,
.label = "vout2 DIMM VRD1"
},
{
.reg = DIMM_VRD2_VOLT,
.label = "vout3 DIMM VRD2"
},
{
.reg = RCA_VRD_VOLT,
.label = "vout4 RCA VRD"
},
};
static const struct smpro_sensor curr_sensor[] = {
{
.reg = CORE_VRD_CURR,
.label = "iout1 CORE VRD"
},
{
.reg = SOC_VRD_CURR,
.label = "iout2 SoC VRD"
},
{
.reg = DIMM_VRD1_CURR,
.label = "iout3 DIMM VRD1"
},
{
.reg = DIMM_VRD2_CURR,
.label = "iout4 DIMM VRD2"
},
{
.reg = RCA_VRD_CURR,
.label = "iout5 RCA VRD"
},
};
static const struct smpro_sensor power[] = {
{
.reg = CORE_VRD_PWR,
.reg_ext = CORE_VRD_PWR_MW,
.label = "power1 CORE VRD"
},
{
.reg = SOC_PWR,
.reg_ext = SOC_PWR_MW,
.label = "power2 SoC"
},
{
.reg = DIMM_VRD1_PWR,
.reg_ext = DIMM_VRD1_PWR_MW,
.label = "power3 DIMM VRD1"
},
{
.reg = DIMM_VRD2_PWR,
.reg_ext = DIMM_VRD2_PWR_MW,
.label = "power4 DIMM VRD2"
},
{
.reg = RCA_VRD_PWR,
.reg_ext = RCA_VRD_PWR_MW,
.label = "power5 RCA VRD"
},
};
static int smpro_read_temp(struct device *dev, u32 attr, int channel, long *val)
{
struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
unsigned int value;
int ret;
switch (attr) {
case hwmon_temp_input:
ret = regmap_read(hwmon->regmap, temperature[channel].reg, &value);
if (ret)
return ret;
break;
case hwmon_temp_crit:
ret = regmap_read(hwmon->regmap, temperature[channel].reg_ext, &value);
if (ret)
return ret;
break;
default:
return -EOPNOTSUPP;
}
*val = sign_extend32(value, 8) * 1000;
return 0;
}
static int smpro_read_in(struct device *dev, u32 attr, int channel, long *val)
{
struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
unsigned int value;
int ret;
switch (attr) {
case hwmon_in_input:
ret = regmap_read(hwmon->regmap, voltage[channel].reg, &value);
if (ret < 0)
return ret;
/* 15-bit value in 1mV */
*val = value & 0x7fff;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int smpro_read_curr(struct device *dev, u32 attr, int channel, long *val)
{
struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
unsigned int value;
int ret;
switch (attr) {
case hwmon_curr_input:
ret = regmap_read(hwmon->regmap, curr_sensor[channel].reg, &value);
if (ret < 0)
return ret;
/* Scale reported by the hardware is 1mA */
*val = value & 0x7fff;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int smpro_read_power(struct device *dev, u32 attr, int channel, long *val_pwr)
{
struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
unsigned int val = 0, val_mw = 0;
int ret;
switch (attr) {
case hwmon_power_input:
ret = regmap_read(hwmon->regmap, power[channel].reg, &val);
if (ret)
return ret;
ret = regmap_read(hwmon->regmap, power[channel].reg_ext, &val_mw);
if (ret)
return ret;
/* 10-bit value */
*val_pwr = (val & 0x3ff) * 1000000 + (val_mw & 0x3ff) * 1000;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int smpro_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_temp:
return smpro_read_temp(dev, attr, channel, val);
case hwmon_in:
return smpro_read_in(dev, attr, channel, val);
case hwmon_power:
return smpro_read_power(dev, attr, channel, val);
case hwmon_curr:
return smpro_read_curr(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int smpro_read_string(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_label:
*str = temperature[channel].label;
return 0;
default:
break;
}
break;
case hwmon_in:
switch (attr) {
case hwmon_in_label:
*str = voltage[channel].label;
return 0;
default:
break;
}
break;
case hwmon_curr:
switch (attr) {
case hwmon_curr_label:
*str = curr_sensor[channel].label;
return 0;
default:
break;
}
break;
case hwmon_power:
switch (attr) {
case hwmon_power_label:
*str = power[channel].label;
return 0;
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static umode_t smpro_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct smpro_hwmon *hwmon = data;
unsigned int value;
int ret;
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_label:
case hwmon_temp_crit:
ret = regmap_read(hwmon->regmap, temperature[channel].reg, &value);
if (ret || value == 0xFFFF)
return 0;
break;
default:
break;
}
break;
default:
break;
}
return 0444;
}
static const struct hwmon_channel_info *smpro_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),
NULL
};
static const struct hwmon_ops smpro_hwmon_ops = {
.is_visible = smpro_is_visible,
.read = smpro_read,
.read_string = smpro_read_string,
};
static const struct hwmon_chip_info smpro_chip_info = {
.ops = &smpro_hwmon_ops,
.info = smpro_info,
};
static int smpro_hwmon_probe(struct platform_device *pdev)
{
struct smpro_hwmon *hwmon;
struct device *hwmon_dev;
hwmon = devm_kzalloc(&pdev->dev, sizeof(struct smpro_hwmon), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!hwmon->regmap)
return -ENODEV;
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "smpro_hwmon",
hwmon, &smpro_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct platform_driver smpro_hwmon_driver = {
.probe = smpro_hwmon_probe,
.driver = {
.name = "smpro-hwmon",
},
};
module_platform_driver(smpro_hwmon_driver);
MODULE_AUTHOR("Thu Nguyen <thu@os.amperecomputing.com>");
MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
MODULE_DESCRIPTION("Ampere Altra SMPro hwmon driver");
MODULE_LICENSE("GPL");
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/hwmon-vid.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/acpi.h> #include <linux/acpi.h>
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define _LINUX_HWMON_SYSFS_H #define _LINUX_HWMON_SYSFS_H
#include <linux/device.h> #include <linux/device.h>
#include <linux/kstrtox.h>
struct sensor_device_attribute{ struct sensor_device_attribute{
struct device_attribute dev_attr; struct device_attribute dev_attr;
......
...@@ -29,18 +29,17 @@ struct gsc_hwmon_channel { ...@@ -29,18 +29,17 @@ struct gsc_hwmon_channel {
/** /**
* struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver * struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver
* @channels: pointer to array of gsc_hwmon_channel structures
* describing channels
* @nchannels: number of elements in @channels array * @nchannels: number of elements in @channels array
* @vreference: voltage reference (mV) * @vreference: voltage reference (mV)
* @resolution: ADC bit resolution * @resolution: ADC bit resolution
* @fan_base: register base for FAN controller * @fan_base: register base for FAN controller
* @channels: array of gsc_hwmon_channel structures describing channels
*/ */
struct gsc_hwmon_platform_data { struct gsc_hwmon_platform_data {
const struct gsc_hwmon_channel *channels;
int nchannels; int nchannels;
unsigned int resolution; unsigned int resolution;
unsigned int vreference; unsigned int vreference;
unsigned int fan_base; unsigned int fan_base;
struct gsc_hwmon_channel channels[];
}; };
#endif #endif
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