mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
hwmon updates for v6.2 merge window
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 Various other minor fixes and improvements -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmOXLDcACgkQyx8mb86f mYFgTw/9GDRiUbSfEYHIrQuao4B8t1QKqdDQbGtM6k0O8DU/CweeMoSWbiAqapFm e/G6ae7zTFWYJAVlvgi11P2dby+F8+vx25sxBhUaUu86dijU+H4JqACLnAMDO27h alXQyOutLcFz1f7rumo89kbMMwvcOZdMTZAILspOGzt5eYKM1+FVjmDaXb3x8MSZ 88QxWltjFH/EhEHi41Djwr/r7ZCz8RdeAev5bKIjfC/KYmCAgeHVz/M7iOEyqjQj 9WWOvjVfKy25gKvfHO1kZF6+9pGFk+Q9ahxWeiBuEhanirE4Y8fGPpnW6aebmyba Ya7V7vEBVC15Egzo0ne/289ps7roB/ee6vbfQuQQCvJhJJZOn8n2QcSvDgqLFE4+ dS5VX3AQ8BVDuVLmD5ZMfX0DHkoPxFOODySZiiWggO3Cf2cJMGb1kjtTYip2BkpI hCNrsMK+DyQ3StRfWMm6tdx+9PRVCue1OL2lGhcyDFifSekdFKshMLTPQW0bxxsx y/nbL0pPuzXCzgfr+FreK3JrqVwiKuUXa9N2vJDst8YAmW4IYd4yMPytOsURnugd xcwNDqn2wcNHIYDSdfby7Y3sI20DiNqVW4qTnP+pfqEG6qsvuaV5yiT+DA7xO7Ur fqSPsqDLUyEQ4aDoVnSsYlabpAFTDuhedM95rwLJ7ds8CqobiRY= =O7h4 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
4d03390b5c
@ -39,7 +39,7 @@ current.
|
||||
|
||||
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,
|
||||
voltage and current.
|
||||
voltage and current. Flow sensor pulses are also available.
|
||||
|
||||
The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally,
|
||||
sixteen virtual temperature sensors of the Farbwerk 360 are exposed.
|
||||
@ -62,7 +62,9 @@ Sysfs entries
|
||||
|
||||
================ ==============================================================
|
||||
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)
|
||||
fan5_pulses Quadro flow sensor pulses
|
||||
power[1-8]_input Pump/fan power (in micro Watts)
|
||||
in[0-7]_input Pump/fan voltage (in milli Volts)
|
||||
curr[1-8]_input Pump/fan current (in milli Amperes)
|
||||
|
@ -160,6 +160,7 @@ Hardware Monitoring Kernel Drivers
|
||||
nzxt-kraken2
|
||||
nzxt-smart2
|
||||
occ
|
||||
oxp-sensors
|
||||
pc87360
|
||||
pc87427
|
||||
pcf8591
|
||||
@ -187,6 +188,7 @@ Hardware Monitoring Kernel Drivers
|
||||
sis5595
|
||||
sl28cpld
|
||||
smm665
|
||||
smpro-hwmon
|
||||
smsc47b397
|
||||
smsc47m192
|
||||
smsc47m1
|
||||
|
44
Documentation/hwmon/oxp-sensors.rst
Normal file
44
Documentation/hwmon/oxp-sensors.rst
Normal file
@ -0,0 +1,44 @@
|
||||
.. 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.
|
102
Documentation/hwmon/smpro-hwmon.rst
Normal file
102
Documentation/hwmon/smpro-hwmon.rst
Normal file
@ -0,0 +1,102 @@
|
||||
.. 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
|
||||
F: drivers/mtd/nand/onenand/
|
||||
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
|
||||
M: Harvey Hunt <harveyhuntnexus@gmail.com>
|
||||
L: linux-mips@vger.kernel.org
|
||||
|
@ -67,6 +67,14 @@ config SENSORS_ABITUGURU3
|
||||
This driver can also be built as a module. If so, the module
|
||||
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
|
||||
tristate "Analog Devices AD7314 and compatibles"
|
||||
depends on SPI
|
||||
@ -799,6 +807,7 @@ config SENSORS_IT87
|
||||
config SENSORS_JC42
|
||||
tristate "JEDEC JC42.4 compliant memory module temperature sensors"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here, you get support for JEDEC JC42.4 compliant
|
||||
temperature sensors, which are used on many DDR3 memory modules for
|
||||
@ -1607,6 +1616,17 @@ config SENSORS_NZXT_SMART2
|
||||
|
||||
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
|
||||
tristate "Philips PCF8591 ADC/DAC"
|
||||
depends on I2C
|
||||
|
@ -167,6 +167,7 @@ obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
|
||||
obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.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_PC87427) += pc87427.o
|
||||
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
|
||||
@ -187,6 +188,7 @@ obj-$(CONFIG_SENSORS_SHT4x) += sht4x.o
|
||||
obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o
|
||||
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
|
||||
obj-$(CONFIG_SENSORS_SMM665) += smm665.o
|
||||
obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
||||
|
@ -26,14 +26,12 @@
|
||||
/**
|
||||
* struct adm1177_state - driver instance specific data
|
||||
* @client: pointer to i2c client
|
||||
* @reg: regulator info for the power supply of the device
|
||||
* @r_sense_uohm: current sense resistor value
|
||||
* @alert_threshold_ua: current limit for shutdown
|
||||
* @vrange_high: internal voltage divider
|
||||
*/
|
||||
struct adm1177_state {
|
||||
struct i2c_client *client;
|
||||
struct regulator *reg;
|
||||
u32 r_sense_uohm;
|
||||
u32 alert_threshold_ua;
|
||||
bool vrange_high;
|
||||
@ -189,13 +187,6 @@ static const struct hwmon_chip_info adm1177_chip_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)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
@ -210,21 +201,9 @@ static int adm1177_probe(struct i2c_client *client)
|
||||
|
||||
st->client = client;
|
||||
|
||||
st->reg = devm_regulator_get_optional(&client->dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
if (PTR_ERR(st->reg) == -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;
|
||||
}
|
||||
ret = devm_regulator_get_enable_optional(&client->dev, "vref");
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",
|
||||
&st->r_sense_uohm))
|
||||
|
@ -289,8 +289,7 @@ static const struct hwmon_chip_info aht10_chip_info = {
|
||||
.info = aht10_info,
|
||||
};
|
||||
|
||||
static int aht10_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *aht10_id)
|
||||
static int aht10_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *device = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
@ -336,7 +335,7 @@ static struct i2c_driver aht10_driver = {
|
||||
.driver = {
|
||||
.name = "aht10",
|
||||
},
|
||||
.probe = aht10_probe,
|
||||
.probe_new = aht10_probe,
|
||||
.id_table = aht10_id,
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ static u8 secondary_ctrl_report[] = {
|
||||
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_DISCONNECTED 0x7FFF
|
||||
#define AQC_FAN_PERCENT_OFFSET 0x00
|
||||
@ -68,62 +68,81 @@ static u8 secondary_ctrl_report[] = {
|
||||
#define AQC_FAN_POWER_OFFSET 0x06
|
||||
#define AQC_FAN_SPEED_OFFSET 0x08
|
||||
|
||||
/* Register offsets for the D5 Next pump */
|
||||
#define D5NEXT_POWER_CYCLES 0x18
|
||||
#define D5NEXT_COOLANT_TEMP 0x57
|
||||
/* Specs of the D5 Next pump */
|
||||
#define D5NEXT_NUM_FANS 2
|
||||
#define D5NEXT_NUM_SENSORS 1
|
||||
#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_FAN_OFFSET 0x5f
|
||||
#define D5NEXT_5V_VOLTAGE 0x39
|
||||
#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 };
|
||||
|
||||
/* Pump and fan speed registers in D5 Next control report (from 0-100%) */
|
||||
static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
|
||||
/* Control report offsets for the D5 Next pump */
|
||||
#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_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_SENSOR_START 0x32
|
||||
#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
|
||||
|
||||
/* Register offsets for the Octo fan controller */
|
||||
#define OCTO_POWER_CYCLES 0x18
|
||||
/* Control report offsets for the Farbwerk 360 */
|
||||
#define FARBWERK360_TEMP_CTRL_OFFSET 0x8
|
||||
|
||||
/* Specs of the Octo fan controller */
|
||||
#define OCTO_NUM_FANS 8
|
||||
#define OCTO_NUM_SENSORS 4
|
||||
#define OCTO_SENSOR_START 0x3D
|
||||
#define OCTO_NUM_VIRTUAL_SENSORS 16
|
||||
#define OCTO_VIRTUAL_SENSORS_START 0x45
|
||||
#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 };
|
||||
|
||||
/* 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 };
|
||||
|
||||
/* Register offsets for the Quadro fan controller */
|
||||
#define QUADRO_POWER_CYCLES 0x18
|
||||
/* Specs of Quadro fan controller */
|
||||
#define QUADRO_NUM_FANS 4
|
||||
#define QUADRO_NUM_SENSORS 4
|
||||
#define QUADRO_SENSOR_START 0x34
|
||||
#define QUADRO_NUM_VIRTUAL_SENSORS 16
|
||||
#define QUADRO_VIRTUAL_SENSORS_START 0x3c
|
||||
#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
|
||||
static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
|
||||
|
||||
/* Fan speed registers in Quadro control report (from 0-100%) */
|
||||
static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 };
|
||||
/* Control report offsets for the Quadro */
|
||||
#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
|
||||
|
||||
/* Sensor report offsets for the High Flow Next */
|
||||
#define HIGHFLOWNEXT_SENSOR_START 85
|
||||
#define HIGHFLOWNEXT_FLOW 81
|
||||
#define HIGHFLOWNEXT_WATER_QUALITY 89
|
||||
@ -282,8 +301,10 @@ struct aqc_data {
|
||||
int temp_sensor_start_offset;
|
||||
int num_virtual_temp_sensors;
|
||||
int virtual_temp_sensor_start_offset;
|
||||
u16 temp_ctrl_offset;
|
||||
u16 power_cycle_count_offset;
|
||||
u8 flow_sensor_offset;
|
||||
u8 flow_pulses_ctrl_offset;
|
||||
|
||||
/* General info, same across all devices */
|
||||
u32 serial_number[2];
|
||||
@ -365,8 +386,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Refreshes the control buffer and returns value at offset */
|
||||
static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
|
||||
/* Refreshes the control buffer and stores value at offset in val */
|
||||
static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -376,7 +397,7 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
ret = get_unaligned_be16(priv->buffer + offset);
|
||||
*val = (s16)get_unaligned_be16(priv->buffer + offset);
|
||||
|
||||
unlock_and_return:
|
||||
mutex_unlock(&priv->mutex);
|
||||
@ -393,7 +414,7 @@ static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val)
|
||||
if (ret < 0)
|
||||
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);
|
||||
|
||||
@ -408,8 +429,28 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
|
||||
|
||||
switch (type) {
|
||||
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)
|
||||
return 0444;
|
||||
switch (attr) {
|
||||
case hwmon_temp_label:
|
||||
case hwmon_temp_input:
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
if (priv->fan_ctrl_offsets && channel < priv->num_fans) {
|
||||
@ -422,20 +463,34 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
|
||||
}
|
||||
break;
|
||||
case hwmon_fan:
|
||||
switch (priv->kind) {
|
||||
case highflownext:
|
||||
/* Special case to support flow sensor, water quality and conductivity */
|
||||
if (channel < 3)
|
||||
return 0444;
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
case hwmon_fan_label:
|
||||
switch (priv->kind) {
|
||||
case highflownext:
|
||||
/* Special case to support flow sensor, water quality
|
||||
* and conductivity
|
||||
*/
|
||||
if (channel < 3)
|
||||
return 0444;
|
||||
break;
|
||||
case quadro:
|
||||
/* Special case to support flow sensor */
|
||||
if (channel < priv->num_fans + 1)
|
||||
return 0444;
|
||||
break;
|
||||
default:
|
||||
if (channel < priv->num_fans)
|
||||
return 0444;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case quadro:
|
||||
/* Special case to support flow sensor */
|
||||
if (channel < priv->num_fans + 1)
|
||||
return 0444;
|
||||
case hwmon_fan_pulses:
|
||||
/* Special case for Quadro flow sensor */
|
||||
if (priv->kind == quadro && channel == priv->num_fans)
|
||||
return 0644;
|
||||
break;
|
||||
default:
|
||||
if (channel < priv->num_fans)
|
||||
return 0444;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -492,20 +547,46 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
if (priv->temp_input[channel] == -ENODATA)
|
||||
return -ENODATA;
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
if (priv->temp_input[channel] == -ENODATA)
|
||||
return -ENODATA;
|
||||
|
||||
*val = priv->temp_input[channel];
|
||||
*val = priv->temp_input[channel];
|
||||
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:
|
||||
*val = priv->speed_input[channel];
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
*val = priv->speed_input[channel];
|
||||
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:
|
||||
*val = priv->power_input[channel];
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
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)
|
||||
return ret;
|
||||
|
||||
@ -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);
|
||||
|
||||
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:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
@ -597,10 +705,10 @@ static const struct hwmon_ops aqc_hwmon_ops = {
|
||||
|
||||
static const struct hwmon_channel_info *aqc_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
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_OFFSET,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
|
||||
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,
|
||||
@ -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_PULSES,
|
||||
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)
|
||||
priv->num_fans = D5NEXT_NUM_FANS;
|
||||
priv->fan_sensor_offsets = d5next_sensor_fan_offsets;
|
||||
priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
|
||||
|
||||
priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
|
||||
priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS;
|
||||
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->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
|
||||
|
||||
priv->temp_label = label_d5next_temp;
|
||||
priv->virtual_temp_label = label_virtual_temp_sensors;
|
||||
priv->speed_label = label_d5next_speeds;
|
||||
@ -865,18 +977,24 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
priv->kind = farbwerk;
|
||||
|
||||
priv->num_fans = 0;
|
||||
|
||||
priv->num_temp_sensors = FARBWERK_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = FARBWERK_SENSOR_START;
|
||||
|
||||
priv->temp_label = label_temp_sensors;
|
||||
break;
|
||||
case USB_PRODUCT_ID_FARBWERK360:
|
||||
priv->kind = farbwerk360;
|
||||
|
||||
priv->num_fans = 0;
|
||||
|
||||
priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
|
||||
priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS;
|
||||
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->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)
|
||||
priv->num_fans = OCTO_NUM_FANS;
|
||||
priv->fan_sensor_offsets = octo_sensor_fan_offsets;
|
||||
priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
|
||||
|
||||
priv->num_temp_sensors = OCTO_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = OCTO_SENSOR_START;
|
||||
priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS;
|
||||
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->power_cycle_count_offset = OCTO_POWER_CYCLES;
|
||||
|
||||
priv->temp_label = label_temp_sensors;
|
||||
priv->virtual_temp_label = label_virtual_temp_sensors;
|
||||
priv->speed_label = label_fan_speed;
|
||||
@ -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->fan_sensor_offsets = quadro_sensor_fan_offsets;
|
||||
priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
|
||||
|
||||
priv->num_temp_sensors = QUADRO_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
|
||||
priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS;
|
||||
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->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->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)
|
||||
priv->kind = highflownext;
|
||||
|
||||
priv->num_fans = 0;
|
||||
|
||||
priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START;
|
||||
|
||||
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
|
||||
|
||||
priv->temp_label = label_highflownext_temp_sensors;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -55,6 +55,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
|
||||
|
||||
/*
|
||||
* 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
|
||||
* earlier (in jiffies).
|
||||
* @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");
|
||||
* @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.
|
||||
* Otherwise, temp_data holds coretemp data.
|
||||
* @valid: If this is 1, the current temperature is valid.
|
||||
*/
|
||||
struct temp_data {
|
||||
int temp;
|
||||
int ttarget;
|
||||
int tjmax;
|
||||
unsigned long last_updated;
|
||||
unsigned int cpu;
|
||||
@ -76,7 +76,6 @@ struct temp_data {
|
||||
u32 status_reg;
|
||||
int attr_size;
|
||||
bool is_pkg_data;
|
||||
bool valid;
|
||||
struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
|
||||
char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
|
||||
struct attribute *attrs[TOTAL_ATTRS + 1];
|
||||
@ -95,85 +94,6 @@ struct platform_data {
|
||||
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 {
|
||||
unsigned int device;
|
||||
int tjmax;
|
||||
@ -340,20 +260,25 @@ static bool cpu_has_tjmax(struct cpuinfo_x86 *c)
|
||||
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;
|
||||
u32 eax, edx;
|
||||
u32 val;
|
||||
|
||||
/* use static tjmax once it is set */
|
||||
if (tdata->tjmax)
|
||||
return tdata->tjmax;
|
||||
|
||||
/*
|
||||
* A new feature of current Intel(R) processors, the
|
||||
* 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 (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 {
|
||||
val = (eax >> 16) & 0xff;
|
||||
/*
|
||||
@ -369,14 +294,133 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
if (force_tjmax) {
|
||||
dev_notice(dev, "TjMax forced to %d degrees C by user\n",
|
||||
force_tjmax);
|
||||
return force_tjmax * 1000;
|
||||
tdata->tjmax = force_tjmax * 1000;
|
||||
} else {
|
||||
/*
|
||||
* An assumption is made for early CPUs and unreadable MSR.
|
||||
* NOTE: the calculated value may not be correct.
|
||||
*/
|
||||
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;
|
||||
|
||||
/*
|
||||
* An assumption is made for early CPUs and unreadable MSR.
|
||||
* NOTE: the calculated value may not be correct.
|
||||
* ttarget is valid only if tjmax can be retrieved from
|
||||
* MSR_IA32_TEMPERATURE_TARGET
|
||||
*/
|
||||
return adjust_tjmax(c, id, dev);
|
||||
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,
|
||||
@ -490,23 +534,17 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
/* We can access status register. Get Critical Temperature */
|
||||
tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
|
||||
/* Make sure tdata->tjmax is a valid indicator for dynamic/static tjmax */
|
||||
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 this
|
||||
* register. Atoms don't have the register at all.
|
||||
* The target temperature is available on older CPUs but not in the
|
||||
* MSR_IA32_TEMPERATURE_TARGET register. Atoms don't have the register
|
||||
* at all.
|
||||
*/
|
||||
if (c->x86_model > 0xe && c->x86_model != 0x1c) {
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET,
|
||||
&eax, &edx);
|
||||
if (!err) {
|
||||
tdata->ttarget
|
||||
= tdata->tjmax - ((eax >> 8) & 0xff) * 1000;
|
||||
if (c->x86_model > 0xe && c->x86_model != 0x1c)
|
||||
if (get_ttarget(tdata, &pdev->dev) >= 0)
|
||||
tdata->attr_size++;
|
||||
}
|
||||
}
|
||||
|
||||
pdata->core_data[attr_no] = tdata;
|
||||
|
||||
|
@ -1447,9 +1447,10 @@ static int __init i8k_init(void)
|
||||
*/
|
||||
if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
|
||||
i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
|
||||
pr_err("unable to get SMM Dell signature\n");
|
||||
if (!force)
|
||||
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,
|
||||
|
@ -269,7 +269,7 @@ static ssize_t update_interval_show(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
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,
|
||||
|
@ -16,7 +16,6 @@ static const unsigned short
|
||||
emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END };
|
||||
|
||||
#define EMC2305_REG_DRIVE_FAIL_STATUS 0x27
|
||||
#define EMC2305_REG_DEVICE 0xfd
|
||||
#define EMC2305_REG_VENDOR 0xfe
|
||||
#define EMC2305_FAN_MAX 0xff
|
||||
#define EMC2305_FAN_MIN 0x00
|
||||
@ -172,22 +171,12 @@ static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned l
|
||||
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;
|
||||
struct emc2305_data *data = cdev->devdata;
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
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);
|
||||
|
||||
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
|
||||
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 = {
|
||||
.get_max_state = emc2305_get_max_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
|
||||
*/
|
||||
if (data->cdev_data[cdev_idx].last_hwmon_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);
|
||||
return 0;
|
||||
}
|
||||
@ -518,13 +528,13 @@ static int emc2305_identify(struct device *dev)
|
||||
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 device *dev = &client->dev;
|
||||
struct emc2305_data *data;
|
||||
struct emc2305_platform_data *pdata;
|
||||
int vendor, device;
|
||||
int vendor;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -535,10 +545,6 @@ static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *
|
||||
if (vendor != EMC2305_VENDOR)
|
||||
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);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
@ -607,7 +613,7 @@ static struct i2c_driver emc2305_driver = {
|
||||
.driver = {
|
||||
.name = "emc2305",
|
||||
},
|
||||
.probe = emc2305_probe,
|
||||
.probe_new = emc2305_probe,
|
||||
.remove = emc2305_remove,
|
||||
.id_table = emc2305_ids,
|
||||
.address_list = emc2305_normal_i2c,
|
||||
|
@ -1083,9 +1083,9 @@ static int fschmd_detect(struct i2c_client *client,
|
||||
static int fschmd_probe(struct i2c_client *client)
|
||||
{
|
||||
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" };
|
||||
const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
|
||||
static const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
|
||||
int i, err;
|
||||
enum chips kind = i2c_match_id(fschmd_id, client)->driver_data;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
@ -257,13 +257,10 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
|
||||
if (nchannels == 0)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pdata = devm_kzalloc(dev,
|
||||
sizeof(*pdata) + nchannels * sizeof(*ch),
|
||||
pdata = devm_kzalloc(dev, struct_size(pdata, channels, nchannels),
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ch = (struct gsc_hwmon_channel *)(pdata + 1);
|
||||
pdata->channels = ch;
|
||||
pdata->nchannels = nchannels;
|
||||
|
||||
/* fan controller base address */
|
||||
@ -277,6 +274,7 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
|
||||
|
||||
of_node_put(fan);
|
||||
|
||||
ch = pdata->channels;
|
||||
/* allocate structures for channels and count instances of each type */
|
||||
device_for_each_child_node(dev, child) {
|
||||
if (fwnode_property_read_string(child, "label", &ch->name)) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
@ -69,6 +69,10 @@ static unsigned short force_id;
|
||||
module_param(force_id, ushort, 0);
|
||||
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];
|
||||
|
||||
#define REG_2E 0x2e /* The register to read/write */
|
||||
@ -563,6 +567,14 @@ struct it87_data {
|
||||
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)
|
||||
{
|
||||
int lsb;
|
||||
@ -2389,7 +2401,6 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
||||
{
|
||||
int err;
|
||||
u16 chip_type;
|
||||
const char *board_vendor, *board_name;
|
||||
const struct it87_devices *config;
|
||||
|
||||
err = superio_enter(sioaddr);
|
||||
@ -2397,7 +2408,13 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
||||
return err;
|
||||
|
||||
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) {
|
||||
case IT8705F_DEVID:
|
||||
@ -2802,24 +2819,9 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
||||
if (sio_data->beep_pin)
|
||||
pr_info("Beeping is supported\n");
|
||||
|
||||
/* Disable specific features based on DMI strings */
|
||||
board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
|
||||
board_name = dmi_get_system_info(DMI_BOARD_NAME);
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* Set values based on DMI matches */
|
||||
if (dmi_data)
|
||||
sio_data->skip_pwm |= dmi_data->skip_pwm;
|
||||
|
||||
exit:
|
||||
superio_exit(sioaddr);
|
||||
@ -3261,8 +3263,10 @@ static int __init it87_device_add(int index, unsigned short address,
|
||||
int err;
|
||||
|
||||
err = acpi_check_resource_conflict(&res);
|
||||
if (err)
|
||||
return err;
|
||||
if (err) {
|
||||
if (!ignore_resource_conflict)
|
||||
return err;
|
||||
}
|
||||
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev)
|
||||
@ -3295,6 +3299,46 @@ exit_device_put:
|
||||
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)
|
||||
{
|
||||
int sioaddr[2] = { REG_2E, REG_4E };
|
||||
@ -3307,6 +3351,8 @@ static int __init sm_it87_init(void)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dmi_check_system(it87_dmi_table);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sioaddr); i++) {
|
||||
memset(&sio_data, 0, sizeof(struct it87_sio_data));
|
||||
isa_address[i] = 0;
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
@ -19,6 +20,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
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? */
|
||||
|
||||
/* Status bits in temperature register */
|
||||
#define JC42_ALARM_CRIT_BIT 15
|
||||
#define JC42_ALARM_MAX_BIT 14
|
||||
#define JC42_ALARM_MIN_BIT 13
|
||||
#define JC42_ALARM_CRIT BIT(15)
|
||||
#define JC42_ALARM_MAX BIT(14)
|
||||
#define JC42_ALARM_MIN BIT(13)
|
||||
|
||||
/* Configuration register defines */
|
||||
#define JC42_CFG_CRIT_ONLY (1 << 2)
|
||||
#define JC42_CFG_TCRIT_LOCK (1 << 6)
|
||||
#define JC42_CFG_EVENT_LOCK (1 << 7)
|
||||
#define JC42_CFG_SHUTDOWN (1 << 8)
|
||||
#define JC42_CFG_HYST_SHIFT 9
|
||||
#define JC42_CFG_HYST_MASK (0x03 << 9)
|
||||
#define JC42_CFG_CRIT_ONLY BIT(2)
|
||||
#define JC42_CFG_TCRIT_LOCK BIT(6)
|
||||
#define JC42_CFG_EVENT_LOCK BIT(7)
|
||||
#define JC42_CFG_SHUTDOWN BIT(8)
|
||||
#define JC42_CFG_HYST_MASK GENMASK(10, 9)
|
||||
|
||||
/* Capabilities */
|
||||
#define JC42_CAP_RANGE (1 << 2)
|
||||
#define JC42_CAP_RANGE BIT(2)
|
||||
|
||||
/* Manufacturer IDs */
|
||||
#define ADT_MANID 0x11d4 /* Analog Devices */
|
||||
@ -199,31 +200,14 @@ static struct jc42_chips jc42_chips[] = {
|
||||
{ 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 */
|
||||
struct jc42_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock; /* protect register access */
|
||||
struct regmap *regmap;
|
||||
bool extended; /* true if extended range supported */
|
||||
bool valid;
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
u16 orig_config; /* original configuration */
|
||||
u16 config; /* current configuration */
|
||||
u16 temp[t_num_temp];/* Temperatures */
|
||||
};
|
||||
|
||||
#define JC42_TEMP_MIN_EXTENDED (-40000)
|
||||
@ -248,85 +232,102 @@ static int jc42_temp_from_reg(s16 reg)
|
||||
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,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct jc42_data *data = jc42_update_device(dev);
|
||||
int temp, hyst;
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
unsigned int regval;
|
||||
int ret, temp, hyst;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
*val = jc42_temp_from_reg(data->temp[t_input]);
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = jc42_temp_from_reg(regval);
|
||||
break;
|
||||
case hwmon_temp_min:
|
||||
*val = jc42_temp_from_reg(data->temp[t_min]);
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP_LOWER, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = jc42_temp_from_reg(regval);
|
||||
break;
|
||||
case hwmon_temp_max:
|
||||
*val = jc42_temp_from_reg(data->temp[t_max]);
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = jc42_temp_from_reg(regval);
|
||||
break;
|
||||
case hwmon_temp_crit:
|
||||
*val = jc42_temp_from_reg(data->temp[t_crit]);
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
|
||||
®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = jc42_temp_from_reg(regval);
|
||||
break;
|
||||
case hwmon_temp_max_hyst:
|
||||
temp = jc42_temp_from_reg(data->temp[t_max]);
|
||||
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
|
||||
>> JC42_CFG_HYST_SHIFT];
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
temp = jc42_temp_from_reg(regval);
|
||||
hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
|
||||
data->config)];
|
||||
*val = temp - hyst;
|
||||
return 0;
|
||||
break;
|
||||
case hwmon_temp_crit_hyst:
|
||||
temp = jc42_temp_from_reg(data->temp[t_crit]);
|
||||
hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
|
||||
>> JC42_CFG_HYST_SHIFT];
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
|
||||
®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
temp = jc42_temp_from_reg(regval);
|
||||
hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
|
||||
data->config)];
|
||||
*val = temp - hyst;
|
||||
return 0;
|
||||
break;
|
||||
case hwmon_temp_min_alarm:
|
||||
*val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1;
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = FIELD_GET(JC42_ALARM_MIN, regval);
|
||||
break;
|
||||
case hwmon_temp_max_alarm:
|
||||
*val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1;
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = FIELD_GET(JC42_ALARM_MAX, regval);
|
||||
break;
|
||||
case hwmon_temp_crit_alarm:
|
||||
*val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1;
|
||||
return 0;
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = FIELD_GET(JC42_ALARM_CRIT, regval);
|
||||
break;
|
||||
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,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned int regval;
|
||||
int diff, hyst;
|
||||
int ret;
|
||||
|
||||
@ -334,21 +335,23 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_min:
|
||||
data->temp[t_min] = jc42_temp_to_reg(val, data->extended);
|
||||
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min],
|
||||
data->temp[t_min]);
|
||||
ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER,
|
||||
jc42_temp_to_reg(val, data->extended));
|
||||
break;
|
||||
case hwmon_temp_max:
|
||||
data->temp[t_max] = jc42_temp_to_reg(val, data->extended);
|
||||
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max],
|
||||
data->temp[t_max]);
|
||||
ret = regmap_write(data->regmap, JC42_REG_TEMP_UPPER,
|
||||
jc42_temp_to_reg(val, data->extended));
|
||||
break;
|
||||
case hwmon_temp_crit:
|
||||
data->temp[t_crit] = jc42_temp_to_reg(val, data->extended);
|
||||
ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit],
|
||||
data->temp[t_crit]);
|
||||
ret = regmap_write(data->regmap, JC42_REG_TEMP_CRITICAL,
|
||||
jc42_temp_to_reg(val, data->extended));
|
||||
break;
|
||||
case hwmon_temp_crit_hyst:
|
||||
ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
|
||||
®val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/*
|
||||
* JC42.4 compliant chips only support four hysteresis values.
|
||||
* Pick best choice and go from there.
|
||||
@ -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
|
||||
: JC42_TEMP_MIN) - 6000,
|
||||
JC42_TEMP_MAX);
|
||||
diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
|
||||
diff = jc42_temp_from_reg(regval) - val;
|
||||
hyst = 0;
|
||||
if (diff > 0) {
|
||||
if (diff < 2250)
|
||||
@ -367,10 +370,9 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
hyst = 3; /* 6.0 degrees C */
|
||||
}
|
||||
data->config = (data->config & ~JC42_CFG_HYST_MASK) |
|
||||
(hyst << JC42_CFG_HYST_SHIFT);
|
||||
ret = i2c_smbus_write_word_swapped(data->client,
|
||||
JC42_REG_CONFIG,
|
||||
data->config);
|
||||
FIELD_PREP(JC42_CFG_HYST_MASK, hyst);
|
||||
ret = regmap_write(data->regmap, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
@ -470,51 +472,80 @@ static const struct hwmon_chip_info jc42_chip_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)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
unsigned int config, cap;
|
||||
struct jc42_data *data;
|
||||
int config, cap;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
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);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP);
|
||||
if (cap < 0)
|
||||
return cap;
|
||||
ret = regmap_read(data->regmap, JC42_REG_CAP, &cap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->extended = !!(cap & JC42_CAP_RANGE);
|
||||
|
||||
if (device_property_read_bool(dev, "smbus-timeout-disable")) {
|
||||
int smbus;
|
||||
|
||||
/*
|
||||
* Not all chips support this register, but from a
|
||||
* quick read of various datasheets no chip appears
|
||||
* incompatible with the below attempt to disable
|
||||
* the timeout. And the whole thing is opt-in...
|
||||
*/
|
||||
smbus = i2c_smbus_read_word_swapped(client, JC42_REG_SMBUS);
|
||||
if (smbus < 0)
|
||||
return smbus;
|
||||
i2c_smbus_write_word_swapped(client, JC42_REG_SMBUS,
|
||||
smbus | SMBUS_STMOUT);
|
||||
ret = regmap_set_bits(data->regmap, JC42_REG_SMBUS,
|
||||
SMBUS_STMOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG);
|
||||
if (config < 0)
|
||||
return config;
|
||||
ret = regmap_read(data->regmap, JC42_REG_CONFIG, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->orig_config = config;
|
||||
if (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;
|
||||
|
||||
@ -535,7 +566,7 @@ static void jc42_remove(struct i2c_client *client)
|
||||
|
||||
config = (data->orig_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)
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
|
||||
data->config |= JC42_CFG_SHUTDOWN;
|
||||
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
|
||||
|
||||
regcache_cache_only(data->regmap, true);
|
||||
regcache_mark_dirty(data->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -555,10 +589,13 @@ static int jc42_resume(struct device *dev)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(data->regmap, false);
|
||||
|
||||
data->config &= ~JC42_CFG_SHUTDOWN;
|
||||
i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
|
||||
data->config);
|
||||
return 0;
|
||||
regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
|
||||
|
||||
/* Restore cached register values to hardware */
|
||||
return regcache_sync(data->regmap);
|
||||
}
|
||||
|
||||
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,
|
||||
/* use integer division instead of equivalent right shift to
|
||||
guarantee arithmetic shift and preserve the sign */
|
||||
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,
|
||||
@ -137,7 +137,7 @@ static ssize_t convrate_show(struct device *dev, struct device_attribute *da,
|
||||
int res;
|
||||
|
||||
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,
|
||||
@ -154,7 +154,7 @@ static ssize_t maxmin_alarm_show(struct device *dev,
|
||||
data->ctrl = ctrl;
|
||||
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:
|
||||
mutex_unlock(&data->lock);
|
||||
|
@ -103,6 +103,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -2663,11 +2664,6 @@ static void lm90_remove_pec(void *dev)
|
||||
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,
|
||||
struct device_node *child,
|
||||
struct lm90_data *data)
|
||||
@ -2749,24 +2745,13 @@ static int lm90_probe(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct hwmon_channel_info *info;
|
||||
struct regulator *regulator;
|
||||
struct device *hwmon_dev;
|
||||
struct lm90_data *data;
|
||||
int err;
|
||||
|
||||
regulator = devm_regulator_get(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);
|
||||
err = devm_regulator_get_enable(dev, "vcc");
|
||||
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);
|
||||
if (!data)
|
||||
|
@ -881,7 +881,7 @@ static int ltc2992_parse_dt(struct ltc2992_state *st)
|
||||
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 ltc2992_state *st;
|
||||
@ -927,7 +927,7 @@ static struct i2c_driver ltc2992_i2c_driver = {
|
||||
.name = "ltc2992",
|
||||
.of_match_table = ltc2992_of_match,
|
||||
},
|
||||
.probe = ltc2992_i2c_probe,
|
||||
.probe_new = ltc2992_i2c_probe,
|
||||
.id_table = ltc2992_i2c_id,
|
||||
};
|
||||
|
||||
|
@ -303,8 +303,7 @@ static const struct hwmon_chip_info max127_chip_info = {
|
||||
.info = max127_info,
|
||||
};
|
||||
|
||||
static int max127_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int max127_probe(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
struct device *hwmon_dev;
|
||||
@ -340,7 +339,7 @@ static struct i2c_driver max127_driver = {
|
||||
.driver = {
|
||||
.name = "max127",
|
||||
},
|
||||
.probe = max127_probe,
|
||||
.probe_new = max127_probe,
|
||||
.id_table = max127_id,
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -1043,7 +1043,9 @@ static struct platform_device *pdev[2];
|
||||
|
||||
static const char * const asus_wmi_boards[] = {
|
||||
"PRO H410T",
|
||||
"ProArt B550-CREATOR",
|
||||
"ProArt X570-CREATOR WIFI",
|
||||
"ProArt Z490-CREATOR 10G",
|
||||
"Pro B550M-C",
|
||||
"Pro WS X570-ACE",
|
||||
"PRIME B360-PLUS",
|
||||
@ -1055,8 +1057,10 @@ static const char * const asus_wmi_boards[] = {
|
||||
"PRIME X570-P",
|
||||
"PRIME X570-PRO",
|
||||
"ROG CROSSHAIR VIII DARK HERO",
|
||||
"ROG CROSSHAIR VIII EXTREME",
|
||||
"ROG CROSSHAIR VIII FORMULA",
|
||||
"ROG CROSSHAIR VIII HERO",
|
||||
"ROG CROSSHAIR VIII HERO (WI-FI)",
|
||||
"ROG CROSSHAIR VIII IMPACT",
|
||||
"ROG STRIX B550-A GAMING",
|
||||
"ROG STRIX B550-E GAMING",
|
||||
@ -1080,8 +1084,11 @@ static const char * const asus_wmi_boards[] = {
|
||||
"ROG STRIX Z490-G GAMING (WI-FI)",
|
||||
"ROG STRIX Z490-H GAMING",
|
||||
"ROG STRIX Z490-I GAMING",
|
||||
"TUF GAMING B550M-E",
|
||||
"TUF GAMING B550M-E (WI-FI)",
|
||||
"TUF GAMING B550M-PLUS",
|
||||
"TUF GAMING B550M-PLUS (WI-FI)",
|
||||
"TUF GAMING B550M-PLUS WIFI II",
|
||||
"TUF GAMING B550-PLUS",
|
||||
"TUF GAMING B550-PLUS WIFI II",
|
||||
"TUF GAMING B550-PRO",
|
||||
|
@ -6,7 +6,6 @@
|
||||
config SENSORS_OCC_P8_I2C
|
||||
tristate "POWER8 OCC through I2C"
|
||||
depends on I2C
|
||||
depends on ARM || ARM64 || COMPILE_TEST
|
||||
select SENSORS_OCC
|
||||
help
|
||||
This option enables support for monitoring sensors provided by the
|
||||
@ -21,7 +20,6 @@ config SENSORS_OCC_P8_I2C
|
||||
config SENSORS_OCC_P9_SBE
|
||||
tristate "POWER9 OCC through SBE"
|
||||
depends on FSI_OCC
|
||||
depends on ARM || ARM64 || COMPILE_TEST
|
||||
select SENSORS_OCC
|
||||
help
|
||||
This option enables support for monitoring sensors provided by the
|
||||
|
284
drivers/hwmon/oxp-sensors.c
Normal file
284
drivers/hwmon/oxp-sensors.c
Normal file
@ -0,0 +1,284 @@
|
||||
// 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 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/kstrtox.h>
|
||||
|
||||
/* Insmod parameters */
|
||||
|
||||
|
@ -23,7 +23,7 @@ enum chips {
|
||||
/* Managers */
|
||||
ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
|
||||
/* Controllers */
|
||||
ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7880,
|
||||
ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880,
|
||||
/* Modules */
|
||||
ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
|
||||
ltm4700,
|
||||
@ -45,15 +45,14 @@ enum chips {
|
||||
#define LTC2974_MFR_IOUT_PEAK 0xd7
|
||||
#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_CLEAR_PEAKS 0xe3
|
||||
#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
|
||||
|
||||
|
||||
/* LTC2975 only */
|
||||
#define LTC2975_MFR_IIN_PEAK 0xc4
|
||||
#define LTC2975_MFR_IIN_MIN 0xc5
|
||||
@ -79,10 +78,11 @@ enum chips {
|
||||
#define LTC3884_ID 0x4C00
|
||||
#define LTC3886_ID 0x4600
|
||||
#define LTC3887_ID 0x4700
|
||||
#define LTC3889_ID 0x4900
|
||||
#define LTC7132_ID 0x4CE0
|
||||
#define LTC7880_ID 0x49E0
|
||||
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
|
||||
#define LTM2987_ID_B 0x8020
|
||||
#define LTC3889_ID 0x4900
|
||||
#define LTC7880_ID 0x49E0
|
||||
#define LTM4664_ID 0x4120
|
||||
#define LTM4675_ID 0x47a0
|
||||
#define LTM4676_ID_REV1 0x4400
|
||||
@ -547,6 +547,7 @@ static const struct i2c_device_id ltc2978_id[] = {
|
||||
{"ltc3886", ltc3886},
|
||||
{"ltc3887", ltc3887},
|
||||
{"ltc3889", ltc3889},
|
||||
{"ltc7132", ltc7132},
|
||||
{"ltc7880", ltc7880},
|
||||
{"ltm2987", ltm2987},
|
||||
{"ltm4664", ltm4664},
|
||||
@ -651,6 +652,8 @@ static int ltc2978_get_id(struct i2c_client *client)
|
||||
return ltc3887;
|
||||
else if (chip_id == LTC3889_ID)
|
||||
return ltc3889;
|
||||
else if (chip_id == LTC7132_ID)
|
||||
return ltc7132;
|
||||
else if (chip_id == LTC7880_ID)
|
||||
return ltc7880;
|
||||
else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B)
|
||||
@ -831,6 +834,7 @@ static int ltc2978_probe(struct i2c_client *client)
|
||||
case ltc3884:
|
||||
case ltc3886:
|
||||
case ltc3889:
|
||||
case ltc7132:
|
||||
case ltc7880:
|
||||
case ltm4664:
|
||||
case ltm4678:
|
||||
@ -902,6 +906,7 @@ static const struct of_device_id ltc2978_of_match[] = {
|
||||
{ .compatible = "lltc,ltc3886" },
|
||||
{ .compatible = "lltc,ltc3887" },
|
||||
{ .compatible = "lltc,ltc3889" },
|
||||
{ .compatible = "lltc,ltc7132" },
|
||||
{ .compatible = "lltc,ltc7880" },
|
||||
{ .compatible = "lltc,ltm2987" },
|
||||
{ .compatible = "lltc,ltm4664" },
|
||||
|
@ -2827,9 +2827,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (pmbus_regulator_is_enabled(rdev) && (status & PB_STATUS_OFF))
|
||||
*flags |= REGULATOR_ERROR_FAIL;
|
||||
if (pmbus_regulator_is_enabled(rdev)) {
|
||||
if (status & PB_STATUS_OFF)
|
||||
*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
|
||||
* defined strictly as fault indicators (not warnings).
|
||||
@ -2851,6 +2855,49 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
|
||||
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)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
@ -2991,6 +3038,7 @@ const struct regulator_ops pmbus_regulator_ops = {
|
||||
.disable = pmbus_regulator_disable,
|
||||
.is_enabled = pmbus_regulator_is_enabled,
|
||||
.get_error_flags = pmbus_regulator_get_error_flags,
|
||||
.get_status = pmbus_regulator_get_status,
|
||||
.get_voltage = pmbus_regulator_get_voltage,
|
||||
.set_voltage = pmbus_regulator_set_voltage,
|
||||
.list_voltage = pmbus_regulator_list_voltage,
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "pmbus.h"
|
||||
|
@ -297,8 +297,7 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sbrmi_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int sbrmi_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
@ -348,7 +347,7 @@ static struct i2c_driver sbrmi_driver = {
|
||||
.name = "sbrmi",
|
||||
.of_match_table = of_match_ptr(sbrmi_of_match),
|
||||
},
|
||||
.probe = sbrmi_probe,
|
||||
.probe_new = sbrmi_probe,
|
||||
.id_table = sbrmi_id,
|
||||
};
|
||||
|
||||
|
@ -199,8 +199,7 @@ static const struct hwmon_chip_info sbtsi_chip_info = {
|
||||
.info = sbtsi_info,
|
||||
};
|
||||
|
||||
static int sbtsi_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int sbtsi_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
@ -239,7 +238,7 @@ static struct i2c_driver sbtsi_driver = {
|
||||
.name = "sbtsi",
|
||||
.of_match_table = of_match_ptr(sbtsi_of_match),
|
||||
},
|
||||
.probe = sbtsi_probe,
|
||||
.probe_new = sbtsi_probe,
|
||||
.id_table = sbtsi_id,
|
||||
};
|
||||
|
||||
|
@ -320,7 +320,7 @@ static ssize_t temp1_limit_show(struct device *dev,
|
||||
u8 index = to_sensor_dev_attr(attr)->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,
|
||||
@ -331,7 +331,7 @@ static ssize_t humidity1_limit_show(struct device *dev,
|
||||
u8 index = to_sensor_dev_attr(attr)->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,
|
||||
if (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,
|
||||
@ -498,7 +498,7 @@ static ssize_t humidity1_alarm_show(struct device *dev,
|
||||
if (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,
|
||||
@ -513,7 +513,7 @@ static ssize_t heater_enable_show(struct device *dev,
|
||||
if (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,
|
||||
@ -550,7 +550,7 @@ static ssize_t update_interval_show(struct device *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]);
|
||||
}
|
||||
|
||||
|
@ -232,8 +232,7 @@ static const struct hwmon_chip_info sht4x_chip_info = {
|
||||
.info = sht4x_info,
|
||||
};
|
||||
|
||||
static int sht4x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *sht4x_id)
|
||||
static int sht4x_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *device = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
@ -292,7 +291,7 @@ static struct i2c_driver sht4x_driver = {
|
||||
.name = "sht4x",
|
||||
.of_match_table = sht4x_of_match,
|
||||
},
|
||||
.probe = sht4x_probe,
|
||||
.probe_new = sht4x_probe,
|
||||
.id_table = sht4x_id,
|
||||
};
|
||||
|
||||
|
466
drivers/hwmon/smpro-hwmon.c
Normal file
466
drivers/hwmon/smpro-hwmon.c
Normal file
@ -0,0 +1,466 @@
|
||||
// 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 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define _LINUX_HWMON_SYSFS_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kstrtox.h>
|
||||
|
||||
struct sensor_device_attribute{
|
||||
struct device_attribute dev_attr;
|
||||
|
@ -29,18 +29,17 @@ struct gsc_hwmon_channel {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @vreference: voltage reference (mV)
|
||||
* @resolution: ADC bit resolution
|
||||
* @fan_base: register base for FAN controller
|
||||
* @channels: array of gsc_hwmon_channel structures describing channels
|
||||
*/
|
||||
struct gsc_hwmon_platform_data {
|
||||
const struct gsc_hwmon_channel *channels;
|
||||
int nchannels;
|
||||
unsigned int resolution;
|
||||
unsigned int vreference;
|
||||
unsigned int fan_base;
|
||||
struct gsc_hwmon_channel channels[];
|
||||
};
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user