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:
Linus Torvalds 2022-12-13 13:09:38 -08:00
commit 4d03390b5c
42 changed files with 1611 additions and 407 deletions

View File

@ -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)

View File

@ -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

View 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.

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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,
};

View File

@ -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;

View File

@ -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>

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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>

View File

@ -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)) {

View File

@ -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>

View File

@ -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;

View File

@ -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, &regval);
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, &regval);
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, &regval);
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,
&regval);
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, &regval);
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,
&regval);
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, &regval);
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, &regval);
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, &regval);
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,
&regval);
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 = {

View File

@ -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);

View File

@ -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)

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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>

View File

@ -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",

View File

@ -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
View 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");

View File

@ -14,6 +14,7 @@
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/kstrtox.h>
/* Insmod parameters */

View File

@ -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" },

View File

@ -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,

View File

@ -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"

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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]);
}

View File

@ -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
View 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");

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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